[EMAIL PROTECTED] 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
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to