pre-PEP: Simple Thunks

peufeu at free.fr peufeu at free.fr
Sat Apr 16 06:50:30 EDT 2005


	I think your proposal is very interesting, I've been missing code blocks  
in Python more and more as time goes by.
	I'll answer to both the 'thunks" proposal and the "suite-based keywords"  
proposal here.

	I find the Ruby syntax rather dirty though, because it has a lot of  
implicit stuff, treats code blocks as different from normal arguments,  
allows passing only one code block, needs a proc keyword, has yield  
execute an implicit block... all this, coming from a Python "explicit is  
better than implicit" background (which makes a lot of sense) is simply  
ugly.
	I like your syntax but have a few comments.
	I'll give you an unordered list of ideas, up to you to do what you like  
with them.

	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 ?

****************************************
	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 }

	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.

	a more complex random example :

	fields['password1'] = edit_field( "Enter Password" )
	fields['password2'] = edit_field( "Enter it again", onsubmit = {|value,  
other_values| if value != other_values['password_1'] then alert('the two  
passwords must be the same !") }

	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() :

	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

***************************************************
	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.

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.
	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 !

	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.

> '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 ?

> 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 ?




More information about the Python-list mailing list