pre-PEP: Suite-Based Keywords

B

Brian Sabbey

Here is a pre-PEP for what I call "suite-based keyword arguments". The
mechanism described here is intended to act as a complement to thunks.
Please let me know what you think.

Suite-Based Keyword Arguments
-----------------------------

Passing complicated arguments to functions is currently awkward in Python.
For example, the typical way to define a class property winds up polluting
the class's namespace with the property's get/set methods. By allowing
keyword arguments to be defined in a suite following a function call,
complicated arguments can be passed in a cleaner, easier way.

Examples
========

Using suite-based keyword arguments, the code

f(x = 1)

is equivalent to

f():
x = 1

In general, a suite following a function call creates a new scope. The
bindings created in this scope get passed to the function as keyword
arguments.

Suite-based keyword arguments can be mixed with regular arguments:

f(1, 2, y = 4):
x = 1

Motivation
==========

One motivation for suite-based keywords is to allow cleaner definitions of
properties. Currently, properties are typically define as in this
example:

class C(object):
def getx(self):
return self.__x
def setx(self, value):
self.__x = value
def delx(self):
del self.__x
x = property(getx, setx, delx, "I'm the 'x' property.")

The 'getx', 'setx', and 'delx' methods get defined in the namespace of the
class even though one wants only to pass them to 'property'. Ideally, one
would want these methods to be defined in their own namespace. Also, it
would be helpful when reading the code if the layout of the code gave
visual indication that their only purpose is to be used in a property.

Using suite-based keyword arguments, and without any changes to the
'property' type, this code can be written as:

class C(object):
x = property():
doc = "I'm the 'x' property."
def fget(self):
return self.__x
def fset(self, value):
self.__x = value
def fdel(self):
del self.__x

Here, 'fget', 'fset' and 'fdel' do not wind up as methods of the class,
and it is visually clear that they are methods only for the 'x' property.
Also, this code is less bug-prone since the name of each method need
appear only once.

Passing callbacks in other situations is made similarly easier and
cleaner:

setHandlers():
def success():
print 'success'
def failure():
print 'an error has occured'
def apocalypse():
print 'a serious error has occured'

a = [1,3,2]
a.sort():
def cmpfunc(x,y):
return cmp(x,y)

Situations that do not require callbacks can also be better organized
using suite-based keywords. For example, here is code as it would
currently be written in Python:

if a:
x = 1
else:
x = 2
f(x=x)

When reading this code, one reaches the 'if' statment without knowing what
its purpose is-- layout of the code does not indicate that the 'if'
statement is calculating an argument to 'f'. Also, it requires a binding
that serves no purpose other than to hold an argument to 'f', yet this
binding persists for the rest of the surrounding function.

Here is the same code using suite-based keyword arguments

f():
if a:
x = 1
else:
x = 2

When reading this code, it is easy to skip over everything that is
involved in calling 'f', if one so desires. Since the suite has its own
namespace, one does not have to worry that the suite creates some bindings
that will be important later in the function.
 
L

Leif K-Brooks

Brian said:
class C(object):
x = property():
doc = "I'm the 'x' property."
def fget(self):
return self.__x
def fset(self, value):
self.__x = value
def fdel(self):
del self.__x

After seeing this example, I think I'll go on a killing spree if your
proposal doesn't get used in Python 2.5 or 2.6. Defining properties like
that would be awesome, only better.
 
J

James Stroud

I_vote_yes(James):
I_understand_what_it_does = True
Makes_code_formatting_way_more_managable_in_tough_cases = True
Makes_code_way_more_readable = True
To_cool = True

Here is a pre-PEP for what I call "suite-based keyword arguments". The
mechanism described here is intended to act as a complement to thunks.
Please let me know what you think.

Suite-Based Keyword Arguments
-----------------------------

Passing complicated arguments to functions is currently awkward in Python.
For example, the typical way to define a class property winds up polluting
the class's namespace with the property's get/set methods. By allowing
keyword arguments to be defined in a suite following a function call,
complicated arguments can be passed in a cleaner, easier way.

Examples
========

Using suite-based keyword arguments, the code

f(x = 1)

is equivalent to

f():
x = 1

In general, a suite following a function call creates a new scope. The
bindings created in this scope get passed to the function as keyword
arguments.

Suite-based keyword arguments can be mixed with regular arguments:

f(1, 2, y = 4):
x = 1

Motivation
==========

One motivation for suite-based keywords is to allow cleaner definitions of
properties. Currently, properties are typically define as in this
example:

class C(object):
def getx(self):
return self.__x
def setx(self, value):
self.__x = value
def delx(self):
del self.__x
x = property(getx, setx, delx, "I'm the 'x' property.")

The 'getx', 'setx', and 'delx' methods get defined in the namespace of the
class even though one wants only to pass them to 'property'. Ideally, one
would want these methods to be defined in their own namespace. Also, it
would be helpful when reading the code if the layout of the code gave
visual indication that their only purpose is to be used in a property.

Using suite-based keyword arguments, and without any changes to the
'property' type, this code can be written as:

class C(object):
x = property():
doc = "I'm the 'x' property."
def fget(self):
return self.__x
def fset(self, value):
self.__x = value
def fdel(self):
del self.__x

Here, 'fget', 'fset' and 'fdel' do not wind up as methods of the class,
and it is visually clear that they are methods only for the 'x' property.
Also, this code is less bug-prone since the name of each method need
appear only once.

Passing callbacks in other situations is made similarly easier and
cleaner:

setHandlers():
def success():
print 'success'
def failure():
print 'an error has occured'
def apocalypse():
print 'a serious error has occured'

a = [1,3,2]
a.sort():
def cmpfunc(x,y):
return cmp(x,y)

Situations that do not require callbacks can also be better organized
using suite-based keywords. For example, here is code as it would
currently be written in Python:

if a:
x = 1
else:
x = 2
f(x=x)

When reading this code, one reaches the 'if' statment without knowing what
its purpose is-- layout of the code does not indicate that the 'if'
statement is calculating an argument to 'f'. Also, it requires a binding
that serves no purpose other than to hold an argument to 'f', yet this
binding persists for the rest of the surrounding function.

Here is the same code using suite-based keyword arguments

f():
if a:
x = 1
else:
x = 2

When reading this code, it is easy to skip over everything that is
involved in calling 'f', if one so desires. Since the suite has its own
namespace, one does not have to worry that the suite creates some bindings
that will be important later in the function.

--
James Stroud, Ph.D.
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA 90095

http://www.jamesstroud.com/
 
K

Kent Johnson

Brian said:
Here is a pre-PEP for what I call "suite-based keyword arguments". The
mechanism described here is intended to act as a complement to thunks.
Please let me know what you think.

Suite-Based Keyword Arguments
-----------------------------

Passing complicated arguments to functions is currently awkward in
Python. For example, the typical way to define a class property winds up
polluting the class's namespace with the property's get/set methods. By
allowing keyword arguments to be defined in a suite following a function
call, complicated arguments can be passed in a cleaner, easier way.

Examples
========

Using suite-based keyword arguments, the code

f(x = 1)

is equivalent to

f():
x = 1

ISTM the syntax is ambiguous. How do you interpret
if f():
x = 1
?

Is a suite alllowed only when a block could not be introduced in the current syntax?

Kent
 
S

Shane Hathaway

Kent said:
ISTM the syntax is ambiguous. How do you interpret
if f():
x = 1
?

Is a suite alllowed only when a block could not be introduced in the
current syntax?

I like this PEP a lot, but your concern is valid. Maybe Brian could
modify the PEP slightly to disambiguate. How about using an ellipsis in
the argument list to signify suite-based keywords? Examples:

f(...):
x = 1

class C(object):
x = property(...):
doc = "I'm the 'x' property."
def fget(self):
return self.__x
def fset(self, value):
self.__x = value
def fdel(self):
del self.__x

d = dict(...):
a = 1
b = 2

Using an ellipsis in a statement that would begin a different kind of
block is illegal and generates a syntax error. Note that this usage
seems to fit well with the definition of "ellipsis".

http://dictionary.reference.com/search?q=ellipsis

Shane
 
B

Bengt Richter

I_vote_yes(James):
I_understand_what_it_does = True
Makes_code_formatting_way_more_managable_in_tough_cases = True
Makes_code_way_more_readable = True
To_cool = True
Kind of cool. If we had full lambdas aka as anonymous defs (def foo(...) with foo left out ;-)
would this be concise sugar for the equivalents shown below your examples?

(The rule for parsing the suite of an anonymous def is that the left column of the first non-space
character of the first suite statement following the def(): becomes the suite indent reference,
and a dedent to the left of that ends the def(): or a closing bracket not opened in the def(): suite
also ends it. Otherwise it is standard suite indentation)

f(**def():
x = 1
return vars())

f(1, 2, y = 4,
**def():
x =1
return vars()) clas C(object):
x = property(
**def():
doc = "I'm the 'x' property."
def fget(self):
return self.__x
def fset(self, value):
self.__x = value
def fdel(self):
del self.__x
return vars())
setHandlers(**def():
def success():
print 'success'
def failure():
print 'an error has occured'
def apocalypse():
print 'a serious error has occured'
return vars())
a = [1,3,2]
a.sort():
def cmpfunc(x,y):
return cmp(x,y)
a.sort(**def():
def cmpfunc(x,y)
return vars()) f(**def():
if a:
x = 1
else:
x = 2
return vars())
Obviously leaving out the "**def(" and the "return vars())" makes it cleaner ;-)

Regards,
Bengt Richter
 
B

Bengt Richter

Here is a pre-PEP for what I call "suite-based keyword arguments". The
mechanism described here is intended to act as a complement to thunks.
Please let me know what you think.
Sorry, I replied to to you via James Stroud's reply (q.v.) ;-)
BTW, I like this suite-based keword definition for calls better
than the thunk stuff ;-)

Regards,
Bengt Richter
 
R

Reinhold Birkenfeld

Brian said:
Here is a pre-PEP for what I call "suite-based keyword arguments". The
mechanism described here is intended to act as a complement to thunks.
Please let me know what you think.

Suite-Based Keyword Arguments
-----------------------------

Passing complicated arguments to functions is currently awkward in Python.
For example, the typical way to define a class property winds up polluting
the class's namespace with the property's get/set methods. By allowing
keyword arguments to be defined in a suite following a function call,
complicated arguments can be passed in a cleaner, easier way.

Examples
========

Using suite-based keyword arguments, the code

f(x = 1)

is equivalent to

f():
x = 1

Pretty cool, but it interferes with current suites.

How would you write

if f(x=1):
print "yes"

using suite-based keyword args?

Reinhold
 
P

peufeu

How would you write

if f(x=1):
print "yes"

using suite-based keyword args?

Good point.
Then we should remove the extra ':' at the end of the function invocation
:

if f(x=@>):
value of x
print "yes"

if f(@**>):
x: value of x
print "yes"
 
K

Kay Schluehr

The idea is interesting but not unambigously realizable. Maybe one
should introduce some extra syntax for disambiguation and thereby
generalize the proposal.

as <specifier>:
# list of definitions and assignments

Proposed specifiers are dict, tuple, *, ** and func.


- as dict:

conversion into a dictionary

Example:

d = as dict:
doc = "I'm the 'x' property."
def fget(self):
return self.__x

We would get d = {"doc":"I'm the 'x' property.", "fget":fget}


- as **:

conversion into keyword-arguments. This comes close in spirit to the
original proposal

Example:

x = property():
as **:
doc = "I'm the 'x' property."
def fget(self):
return self.__x

- as tuple:

conversion into a tuple. Preserving order.

Example:

t = as tuple:
doc = "I'm the 'x' property."
def fget(self):
return self.__x
<function fget at 0x00EC4770>


- as *:

conversion into an argument tuple. Preserving order.

Example:

x = property():
as *:
def get_x():
return self.__x
def set_x(value):
self.__x = value
del_x = None
doc = "I'm the 'x' property."



- as func(*args,**kw):

Anoymus functions. Replacement for lambda. Support for
arbirtray statements?

Examples:

p = as func(x,y): x+y
p(1,2)

i,j = 3,4
if (as func(x,y): x+y) (i,j) > 10:
print True


Ciao,
Kay
 
N

Nicolas Fleury

Shane said:
I like this PEP a lot, but your concern is valid. Maybe Brian could
modify the PEP slightly to disambiguate. How about using an ellipsis in
the argument list to signify suite-based keywords? Examples:

f(...):
x = 1

class C(object):
x = property(...):
doc = "I'm the 'x' property."
def fget(self):
return self.__x
def fset(self, value):
self.__x = value
def fdel(self):
del self.__x

d = dict(...):
a = 1
b = 2

Using an ellipsis in a statement that would begin a different kind of
block is illegal and generates a syntax error. Note that this usage
seems to fit well with the definition of "ellipsis".

http://dictionary.reference.com/search?q=ellipsis

Shane

I like the ellipsis syntax a lot, it greatly helps the proposal IMHO.

Regards,
Nicolas
 
B

Brian Sabbey

Shane said:
I like this PEP a lot, but your concern is valid. Maybe Brian could
modify the PEP slightly to disambiguate. How about using an ellipsis in
the argument list to signify suite-based keywords? Examples:

f(...):
x = 1

class C(object):
x = property(...):
doc = "I'm the 'x' property."
def fget(self):
return self.__x
def fset(self, value):
self.__x = value
def fdel(self):
del self.__x

d = dict(...):
a = 1
b = 2

Using an ellipsis in a statement that would begin a different kind of
block is illegal and generates a syntax error. Note that this usage
seems to fit well with the definition of "ellipsis".

http://dictionary.reference.com/search?q=ellipsis

Shane

Yes, my description of the syntax was ambiguous. To clarify, I intended
the syntax to be backwardly compatible. That is, one would not be able to
use a suite to define keywords if there already exists a suite for other
reasons. So, one would not be able to use a suite to pass keywords to 'f'
in this situation:

if f():
x = 1

This code should behave exactly as it does now.

I agree that the ellipsis idea probably makes the code more readable, and
it may be a good idea to have them for that reason. However, ellipses are
not necessary as a way to disambiguate the syntax; all statements
containing suites currently begin with a keyword, and keyword suites would
not.

-Brian
 
B

Brian Sabbey

Reinhold said:
Pretty cool, but it interferes with current suites.

How would you write

if f(x=1):
print "yes"

using suite-based keyword args?

Reinhold

You wouldn't be able to use suite keywords in that situation. Also, one
would not be able to use suite keywords when there is more than one
function call on the same line:

y = f()*g():
x = 1 # ?? not allowed

There is only so much suites can do. Cases in which you want to do both
are probably far enough between that it seems acceptable to me to require
two suites:

t = f():
x = 1
if t:
y = 1

In general, I think that anything more than just a function call with an
optional assignment should be disallowed:

y = [f()]: # ? probably shouldn't be allowed
x = 1

-Brian
 
B

Brian Sabbey

Bengt said:
Kind of cool. If we had full lambdas aka as anonymous defs (def foo(...) with foo left out ;-)
would this be concise sugar for the equivalents shown below your examples?

(The rule for parsing the suite of an anonymous def is that the left column of the first non-space
character of the first suite statement following the def(): becomes the suite indent reference,
and a dedent to the left of that ends the def(): or a closing bracket not opened in the def(): suite
also ends it. Otherwise it is standard suite indentation)


f(**def():
x = 1
return vars())

Yes, it seems it would just be sugar for full lambdas. Although, in this
example and the others, wouldn't you have to actually call the anonymous
function?

f(**(def():
x = 1
return vars())())

Otherwise it seems like you are trying to pass a function, not the
keywords the function returns.

One could, of course, also do the same with a named function:

def no_longer_anonymous():
x = 1
return vars()

f(**no_longer_anonymous())

-Brian
 
K

Kay Schluehr

Nicolas said:
I like the ellipsis syntax a lot, it greatly helps the proposal IMHO.

Regards,
Nicolas

I think the usage of the block is still limited in Shawns answer. Kents
objection can be evaded but Reinhold Birkenfelds remains valid.

The statement

f(...):
x = 1

is still not an expression like f(x=1) is. I think one has to mark
somehow the scope of the suite-based keywords.

In the examples below we can identifiy the expression belonging to f
without ambiguities and restrictions.

f(y=0,..)
.. x = 1

if f(y=2,..) == 1:
.. x = 5
def g(a):
a**2-1
return 8

Note that the colon after f has vanished as expected for a call.

Ciao,
Kay
 
S

Steven Bethard

Shane said:
I like this PEP a lot, but your concern is valid. Maybe Brian could
modify the PEP slightly to disambiguate. How about using an ellipsis in
the argument list to signify suite-based keywords? Examples:

f(...):
x = 1

class C(object):
x = property(...):
doc = "I'm the 'x' property."
def fget(self):
return self.__x
def fset(self, value):
self.__x = value
def fdel(self):
del self.__x

d = dict(...):
a = 1
b = 2

I also like the ellipsis idea much better than the original proposal.
While I figured out what the original syntax meant after half a minute
of staring at it (unlike the thunk proposal which I still can't decode),
I couldn't help but feel that I'd like a visual cue that arguments had
been omitted. Seeing
f():
gives me only ':' as a tiny clue that arguments have been omitted. And
':' is used in all Python suites, so it doesn't have a good intuition
associated with it. Using ellipsis or something similar to indicate
that keyword arguments have been omitted would make this much easier for
me to read.

STeVe
 
B

Bengt Richter

The idea is interesting but not unambigously realizable. Maybe one
should introduce some extra syntax for disambiguation and thereby
generalize the proposal.
This is intriguing. I am reminded of trailing "where x is something" phrase, but with
an implied x.
as <specifier>:
# list of definitions and assignments
ISTM this list, other than for func, always could be considered to
generate an ordered sequence of (key, value) pairs that need to be plugged into
the preceding context. Where to plug it in is implicit, but it could be given
explicit target name(s) in an expression, e.g.,
<expr> where: <where-suite>

and <where-suite> would be a series of assignments like for suite-based keywords, except that
the key names would be used in resolving the names in the expression preceding the where:, e.g.,
foo(x, y) where:
x = 1
y = 2

would make the foo call using the where bindings by name, here effectively calling foo(1, 2)
But sometimes we want all the bindings in a suite in a chunk, as with suite-based keyword bindings.

I'm proposing below a unary suite operator spelled '::' which will return a single tuple of 2-tuples
representing the ordered bindings produced by the statement suite following the '::' Thus ::<suite>
is a general expression that can be used anywhere an expression can be used. (I'll explain indentation rules).

The "::" expression I'm proposing generalizes capturing suite bindings into an ordered sequence of (key,value)
tuples, like an ordered vars().items() limited to the bindings produced in the suite following "::"
Thus
items = ::
x = 1
y = [1,2]
def foo():pass

print items => (('x', 1), ('y', [1, 2]), ('foo', <function foo at 0x02EE8D14>))

Now we can use a :: expression in a where: spec, e.g.

d = dict(items) where:
items = ('added','value') + :: # note that :: is a unary suite operator producing a tuple of tuples
x = 1
y = [1,2]
def foo():pass

where: actually introduces a suite of scoped assignments much like :: e.g.,
d = {key:value} where: key='x'; value='123' # where is ;-greedy on the same line
print d => {'x':123}

or a multi-parameter call
foo(x, y=23, *args, **kw) where:
x = 333; y = 555
args = [44, 55, 66]
kw = dict:):
z = 'zee'
class C(object): pass
c = C()
del C )

resulting in foo being called like foo(333, 555, 44, 55, 66, z='zee', c=C()) # except C was transiently defined


You can also use a :: expression directly, skipping the where: reference resolution mechanism, e.g.,

d = dict:):
x = 1
y = [1,2]
def foo():pass) # closing bracket not opened in :: suite ends the suite like a sufficient dedent would

It can also participate as a normal expression anywhere a tuple of tuples could go, e.g.

print list(t[0] for t in ::
x = 1
y = [1,2]
def foo():pass)
=> ['x', 'y', 'foo']

Now we can rewrite some examples. Thanks for the inspiration to generalize/orthogonalize ;-)
Proposed specifiers are dict, tuple, *, ** and func.


- as dict:

conversion into a dictionary

Example:

d = as dict:
doc = "I'm the 'x' property."
def fget(self):
return self.__x
d = dict(items) where:
items = ::
doc = "I'm the 'x' property."
def fget(self):
return self.__x

Or more concisely

d = dict:):
doc = "I'm the 'x' property."
def fget(self):
return self.__x )
We would get d = {"doc":"I'm the 'x' property.", "fget":fget}


- as **:

conversion into keyword-arguments. This comes close in spirit to the
original proposal
BTW, this would happen to cover dict alternatively by way of your format
d = dict():
as **: key=value #etc
Example:

x = property():
as **:
doc = "I'm the 'x' property."
def fget(self):
return self.__x
x = property(**kw) where:
kw = dict:):
doc = "I'm the 'x' property."
def fget(self):
return self.__x)
or
x = property(fget=fget, doc=doc) where:
doc = "I'm the 'x' property."
def fget(self):
return self.__x
- as tuple:

conversion into a tuple. Preserving order.

Example:

t = as tuple:
doc = "I'm the 'x' property."
def fget(self):
return self.__x
<function fget at 0x00EC4770>
"as tuple:" is special sugar for
t = tuple(*v) where:
v = (t[1] for t in ::
doc = "I'm the 'x' property."
def fget(self):
return self.__x )
- as *:

conversion into an argument tuple. Preserving order.
That's the same as passing values to tuple above
Example:

x = property():
as *:
def get_x():
return self.__x
def set_x(value):
self.__x = value
del_x = None
doc = "I'm the 'x' property."

x = property(*seq) where:
seq = (item[1] for item in ::
def get_x():
return self.__x
def set_x(value):
self.__x = value
del_x = None
doc = "I'm the 'x' property." )
- as func(*args,**kw):

Anoymus functions. Replacement for lambda. Support for
arbirtray statements?
I prefer dropping combinations of def and/or <name> from def <name>(<arglist>): <suite>
to get

def <name>(<arglist>): <suite> # normal def
def (<arglist>): <suite> # anonymous def
<name>(<arglist>): <suite> # named callable suite, aka named thunk
(<arglist>): <suite> # anonymous callable suite, aka thunk

any of which can be passed to a decorator or bound or passed to functions etc.

The parsing of indentation for the three last definitions is like for the :: unary suite operator
i.e., they are all expressions, not statements, and ::<suite> is an expression that ends where the
<suite> ends, so there are some rules. The first is that a closing bracket not opened within the suite
ends the suite. This is typically the closing paren of a function call, but could be anything, e.g.,
print [:: x=123; y=456] => [(('x', 123), ('y', 456))] # ie the :: value is a single tuple of tuples.

More typical would be the use of def(<arglist>):<suite> as a substitute for lambda <arglist>:<expr>

BTW, note that :: is ;-greedy in a single-line multi-statement suite, so
items = :: x=1; y=2 # => (('x',1), ('y',2))
I.e., it has the effect of
items = ::
x = 1
y = 2
Not
items == ::
x = 1
y = 2

The same greediness goes for "where:"
Examples:

p = as func(x,y): x+y
p(1,2)

p = def(x, y): x+y
i,j = 3,4
if (as func(x,y): x+y) (i,j) > 10:
print True

if (def(x,y): x+y) (i,j) > 10:
print true

I haven't thought of all the uses for anonymous callable suites (thunks), but imagine as callback:
(putting it as an unusual default parameter makes it bind in the same namspace as the definintion of foo)

def foo(x, cb=(y):print y):
cb(x)

foo(3) # => prints 3 and assigns y=3 as local-to-foo-definer side effect (often main effect)

foo(123, (z):print 'Setting z=%r'%z) => prints Setting z=123 and sets z=123 locally to foo caller.

foo(456, lambda *ignore:None) # also legal, foo doesn't know what callable it's being passed


The suite-based keyword call syntax

foo():
x = 1
y = [1,2]
def foo():pass

can be handled with the more general

foo(**kw) where: kw = dict:):
x = 1
y = [1,2]
def foo():pass)

This is a new rev on my thoughts. Expect contradictions with older stuff.
Naturally I like my newest ideas best ;-)

Regards,
Bengt Richter
 
B

Bengt Richter

Yes, it seems it would just be sugar for full lambdas. Although, in this
example and the others, wouldn't you have to actually call the anonymous
function?
D'oh, yes, but you got the idea ;-)
f(**(def():
x = 1
return vars())())

Otherwise it seems like you are trying to pass a function, not the
keywords the function returns.

One could, of course, also do the same with a named function:

def no_longer_anonymous():
x = 1
return vars()

f(**no_longer_anonymous())

Yes, but that does away with the trailing suite definition of the bindings from the pov of f ;-)

BTW, I hope you will read my latest post replying to Kay Schluehr, where I propose some different
spellings ;-)

f():
x = 1

would be spelled

f(**kw) where:
kw = dict:):
x = 1)

or could also be

f(x=x) where:
x = 1

"::" is a unary suite operator that returns an ordered vars().items() representing the net bindings made
in the suite (i.e. del <name> will remove a binding just as for vars()). ::<suite> is a first class expression.

items = ::
x = 1
y = 2
print items => (('x', 1), ('y', 2))

"where:" is an expression trailer introducing a suite like :: except that the bindings are used to evaluate
the names in the expression that the "where:" trails, not to produce an items() tuple.

See the other post replying to Kay for more, superseding some things in my reply to you ;-)

Regards,
Bengt Richter
 
B

Bengt Richter

[...]
Yes, my description of the syntax was ambiguous. To clarify, I intended
the syntax to be backwardly compatible. That is, one would not be able to
use a suite to define keywords if there already exists a suite for other
reasons. So, one would not be able to use a suite to pass keywords to 'f'
in this situation:

if f():
x = 1

This code should behave exactly as it does now.

I agree that the ellipsis idea probably makes the code more readable, and
it may be a good idea to have them for that reason. However, ellipses are
not necessary as a way to disambiguate the syntax; all statements
containing suites currently begin with a keyword, and keyword suites would
not.
Using my expression where: trailer, the above if could be spelled

if f(x) where: x=1: # where:<suite> acts like '' other than its semantics.
do_something()

I was debating whether to use a where: that applied to a whole previous statement and its suite,
indenting as if the where were a following statment, e.g.,

if f(x):
do_something()
where:
x = 1

But I thought that could get too far away, and not cover many use cases that could be done
other ways. The thing that where does though is create a transient namespace for the suite
bindings, so they don't clobber the calling namespace the way just prefixing a setup would

x = 1 # clobber x
if f(x):
do_something()

Shoot, I just has another idea about the ::<suite> expression -- instead of returning a tuple,
it could return an order-maintaining subtype of dict with methods for items(), keys() and values()
and that would make better spellings for many examples in Kay's post ;-/

Also suite-based keyword calling would just become

f(10, 4, **:: # note ease of including other parameters if part of the signature
x = 1
y = 2 )

and you could do
print :):
x = 1
y = 2
).keys()
=> ['x', 'y']

or call foo
foo(self, whatever, *:):
x = 1
y = 2).values())

Oh well, that's the way ideas evolve ;-)

Regards,
Bengt Richter
 
B

Bengt Richter

if f(x=1) where: x=23:
print "yes"

"where:" expression trailer intoduces a suite whose net bindings are used to evaluate the names
in the expression it trails, which means the calling parameters, if the expression is a call.
Syntacticaly where:<suite> should be equivalent to '' other than its name binding effect (which
is only for the call -- i.e.,
x = 123
foo(x) where: x=1 # => foo(1)
print x # => 123
You wouldn't be able to use suite keywords in that situation. Also, one
would not be able to use suite keywords when there is more than one
function call on the same line:

y = f()*g():
x = 1 # ?? not allowed

Stretching for it, using my latest and greatest ;-)

y = f(**::
x = 1
y = 'y for f'
)*g(**::
x = 'x for g'
y = 'y for g'
def foo(): return 'foo for g'
)

Note that there is no problem adding other parameters, because ::<suite> is just
a unary expression returning dict subtype instance, e.g.,

y = f(11,22,**::
x = 1
y = 'y for f'
)*g(*args_from_somewhere, **::
x = 'x for g'
y = 'y for g'
def foo(): return 'foo for g'
)


This assumes that the unary ::<suite> expression returns an ordered dict subtype
instance containing the bindings created in the suite following the unary suite operator ::
E.g,
d = ::
x = 1
y = 2
print d # => {'x':1, 'y':2}

(Ignore version of previous posts where :: returned d.items() in place of d here)
There is only so much suites can do. Cases in which you want to do both
are probably far enough between that it seems acceptable to me to require
two suites:

t = f():
x = 1
if t:
y = 1

It's a little clunky, but

if self.f(self, z, *a) where:
z=123
a = getarglist(): # ':' terminates transparent where:<suite>
y = 1 # indentation as if where: said:
In general, I think that anything more than just a function call with an
optional assignment should be disallowed:

y = [f()]: # ? probably shouldn't be allowed
x = 1

y = [f(**:: x=1)] # sort form with single-line <suite> for ::<suite>
or
y = [f(**::
x = 1
)]

isolates the suite better, in case it has logic and fancy stuff
Regards,
Bengt Richter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top