pre-PEP: Simple Thunks
Brian Sabbey
sabbey at u.washington.edu
Sat Apr 16 20:30:01 EDT 2005
peufeu at free.fr wrote:
>
> Keep in mind that most of the problems come from the "space is
> significant" thing, which is IMHO a very good idea, but prevents us from
> putting code in expressions, like :
>
> func( a,b, def callback( x ):
> print x
> )
>
> or does it ? maybe this syntax could be made to work ?
Hmm. I'd like to think that suite-based keywords do make this example
work. One just has to let go of the idea that all arguments to a function
appear inside parentheses.
> ****************************************
> Comments on the thunks.
>
> First of all I view code blocks as essential to a language. They are
> very useful for a lot of common programming tasks (like defining callbacks in
> an elegant way) :
>
> button = create_button( "Save changes" ):
> do
> self.save()
>
> However it seems your thunks can't take parameters, which to me is a
> big drawback. In ruby a thunk can take parameters. Simple examples :
>
> field = edit_field( "initial value", onsubmit={ |value| if
> self.validate(value) then do something else alert( "the value is invalid" ) }
> )
> [1,3,4].each { |x| puts x }
Thunks can take parameters:
do value in field = edit_field("initial value"):
if self.validate(value):
something
else:
alert("the value is invalid")
But callbacks are better defined with suite-based keywords:
field = edit_field("initial value"):
def onsubmit(value):
if self.validate(value):
something
else:
alert("the value is invalid")
> This has the advantage that the interface to the thunk (ie. its
> parameters) are right there before your eyes instead of being buried in the
> thunk invocation code inside the edit_field.
In the cases in which one is defining a callback, I agree that it would be
preferable to have the thunk parameters not buried next to 'do'.
However, I do not consider thunks a replacement for callbacks. They are a
replacement for code in which callbacks could be used, but normally aren't
because using them would be awkward. Your 'withfile' example below is a
good one. Most people wouldn't bother defining a 'withfile' function,
they would just call 'open' and 'close' individually.
> So I think it's essential that thunks take parameters and return a
> value (to use them effectively as callbacks).
> What shall distinguish them from a simple alteration to def(): which
> returns the function as a value, and an optional name ? really I don't know,
> but it could be the way they handle closures and share local variables with
> the defining scope. Or it could be that there is no need for two kinds of
> function/blocks and so we can reuse the keyword def() :
Right, defining a function with 'def' is different than defining a thunk
because thunks share the namespace of the surrounding function, functions
do not:
x = 1
def f():
x = 2 # <- creates a new x
g(f)
print x # ==> 1
do g():
x = 2
print x # ==> 2 ( assuming 'g' calls the thunk at least once)
>
> If you wish to modify def(), you could do, without creating any
> keyword :
>
> # standard form
> f = def func( params ):
> code
>
> # unnamed code block taking params
> func = def (params):
> code
>
> Note that the two above are equivalent with regard to the variable
> "func", ie. func contains the defined function. Actually I find def
> funcname() to be bloat, as funcname = def() has the same functionality, but
> is a lot more universal.
>
> # unnamed block taking no params
> f = def:
> code
I'm confused. These examples seem to do something different than what a
'do' statement would do. They create a function with a new namespace and
they do not call any "thunk-accepting" function.
> ***************************************************
> Comments on the suite-based keywords.
>
> Did you notice that this was basically a generalized HEREDOC syntax ?
>
> I'd say that explicit is better than implicit, hence...
>
> Your syntax is :
>
> do f(a,b):
> a block
>
> passes block as the last parameter of f.
> I don't like it because it hides stuff.
Yes, it hides stuff. It doesn't seem to me any worse than what is done
with 'self' when calling a method though. Once I got used to 'self'
appearing automatically as the first parameter to a method, it wasn't a
big deal for me.
> I'd write :
>
> f(a,b,@>,@>):
> """a very
> large multi-line
> string"""
> def (x):
> print x
>
> Here the @> is a symbol (use whatever you like) to indicate
> "placeholder for something which is on the next line".
> Indentation indicates that the following lines are indeed argument
> for the function. The : at the end of the function call is there for
> coherence with the rest of the syntax.
> Notice that, this way, there is no need for a "do" keyword, as the
> code block created by an anonymous def() is no different that the other
> parameter, in this case a multiline string.
>
> This has many advantages.
>
> It will make big statements more readable :
>
> instead of :
> f( a,b, [some very big expression made up of nested class
> constructors like a form defintion ], c, d )
>
> write :
> f( a, b, @>, c, d ):
> [the very big expression goes here]
>
> So, independently of code blocks, this already improves the
> readability of big statements.
It might be useful in some situations to be able to define suite-based
arguments by their order rather than their keyword. But, to me, one of
Python's strengths is that it avoids special syntactic characters. So I
can't say I like "@>" very much. Perhaps there is another way of allowing
this.
> You could also use named parameters (various proposals):
>
> f( a,b, c=@>, d=@> ):
> value of c
> value of d
>
> or :
>
> f( a,b, @*> ):
> value of c
> value of d
>
> f( a,b, @**> ):
> c: value of c
> d: value of d
>
> Notice how this mimics f( a,b, * ) and f(a,b, ** ) for multiple
> arguments, and multiple named arguments. Do you like it ? I do. Especially
> the named version where you cant' get lost in the param block because you see
> their names !
I like those ideas, but maybe just leave off the "@>".
f(a,b,c=,d=):
c = 1
d = 2
f(a,b,*)
1
2
f(a,b,**)
c = 1
d = 2
Another problem is that one may create bindings in the suite that should
not be keywords. Explicitly defining the keywords would be useful in this
case too. For example,
f(a,b,c=,d=):
c = [i**2 for i in [1,2]] # 'i' is temporary
d = 2
Here 'i' would not be passed as a keyword argument, because it 'c' and 'd'
are explicitly defined as the only keyword arguments.
>
> Now if you say that def returns the defined function as a value, you
> don't need a do keyword.
>
> So, for instance :
>
> def withfile( fname, thunk, mode = "r" ):
> f = open( fname, mode )
> thunk(f)
> f.close()
>
> then :
>
> withfile( "afile.txt", @>, "w" ):
> def (f):
> f.write( something )
>
> Now, you may say that the def on an extra line is ugly, then just write :
>
> withfile( "afile.txt", @>, "w" ): def (f):
> f.write( something )
>
> If you really like do you can make it a synonym for def and then :
>
> withfile( "afile.txt", @>, "w" ): do (f):
> f.write( something )
>
> The two ":" seem a bit weird but I think they're OK.
>
Again, there is the problem of a new namespace being created when using
'def'. Also, it's annoying to have to use 'def' (even though one could
just put it on the same line).
>> 'break' and 'return' should probably not be allowed in thunks. One
>
> I do think return should be allowed in a thunk. After all it's a
> function block, so why ditch useful functionality ?
> yield should also be allowed, after all why can a thunk not be a
> generator ? This would be powerful.
> def withfile( fname, thunk, mode = "r" ):
> f = open( fname, mode )
> r = thunk(f)
> f.close()
> return r
>
> val = withfile( "afile.txt", @>, "w" ):
> def (f):
> f.write( something )
> return something
>
> Well, it seems I have no more ideas for now.
> What do you think about all this ?
>
Well here you're using a type of suite-based keywords, so 'return' is ok.
Inside thunks I still think 'return' would be confusing. I don't think
one can replace thunks with suite-based keywords.
>> The thunk evaluates in the same frame as the function in which it was
>> defined. This frame is accessible:
>
> Hm ?
> You mean like a function closure, or that local variables defined
> inside the thunk will be visible to the caller ?
> This might change a lot of things and it becomes like a continuation,
> are you going to recode stackless ?
I meant that a thunk is not like a function closure. Variables defined in
the thunk won't be visible to the caller of the thunk (except through
tk_frame), but they will be visible to the function surrounding the thunk.
I made an initial implementation, and it didn't require anything like
stackless.
-Brian
More information about the Python-list
mailing list