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 ?


--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to