[Python-3000] A plea for anonymous functions

Talin talin at acm.org
Thu Nov 16 11:50:50 CET 2006


Fredrik Lundh wrote:
> Talin wrote:
> 
>> The problem with providing use cases is that each one individually 
>> doesn't carry much weight. When I do Javascript/AJAX programming, I use 
>> anonymous functions quite a bit, as in the following example:
>>
>>     do_command( 'get_synset_links', {}, function( response_data ) {
>>        format_expanded( li.content, response_data.related )
>>     })
>>
>> 'do_command' calls a function on the server side via an async HTTP 
>> request, and calls the callback when the data is ready. By using an 
>> anonymous function as the callback, I can group the request for the data 
>> and the use of that data together in a logical way.
> 
> Your specific example would probably be written:
> 
>      server.get_synset_links(
>          lambda rec: format_expanded(li.content, rec.related)
>      )

Only if the code in the block is a one-liner.

> in today's Python, which I find a bit easier to read than your slightly 
> bloated JavaScript example.
> 
> How do you handle errors, btw?

For this app, internally in the do_command function. In any case, error 
handling code can be assumed to have been omitted for clarity.

>> Could I do the same thing using a named callback? Sure. Would it be as 
>> easy to read and as clear?
> 
> Well, if you find that easy to read and clear, I suspect you're more of 
> a JavaScript programmer than a Python programmer.  Here's how a more 
> general form of the above would look in Python:
> 
>      response = (yield server.get_synset_links())
>      format_expanded(li.content, response.related)
> 
> or
> 
>      def handle_response(record):
>          format_expanded(li.content, response.related)
>      server.get_synset_link(handle_response)
> 
> or perhaps
> 
>      with async server.get_synset_link() as response:
>          format_expanded(li.content, response.related)

None of these are functionally equivalent to my code example, if I am 
understanding them correctly.

But I guess I need to explain a bit more about my example.

First, 'get_synset_links' is a URL, not a method name. (More 
technically, it's a fragment of a URL). I suppose you could play tricks 
with __getitem__ to turn a method call into a URL, but I wouldn't do 
that - for one thing, not all URLs can be represented as method names.

More importantly, the flow of execution is different:

    do_command( stuff ) {
       // This code executes when the HTTP response from the
       // server returns.
       ... code ...
    }

    // This code executes immediately after the request has
    // been sent.
    ... code ...

In other words, 'do_command' sends a request to the server and continues 
processing after the block. When the response returns, the code inside 
the block is executed. I don't think you can emulate this behavior with 
either yield or with - they are both synchronous operations.

So I can see how your example #2 might be made to work this way, but not 
#1 or #3. And I'm not convinced that #2 is really better.

More generally, the notion here is to implement a rough kind of 
continuation, based on external asynchronous events. There are lots and 
lots of potential use cases for code that handles asynchronous responses 
to program events.

> (all of which are shorter than your JavaScript example, by the way)
> 
>> But it doesn't matter, because no matter what cases I come up with, 
>> someone will claim "You're just saving 2 lines of code".
> 
> No, I said that under your proposal, you're trading two lines of code 
> that looks like Python for two (or three) lines of code that looks like 
> something else.

Lets not get confused here - I'm illustrating in Javascript code because 
this is something that's easy to do in Javascript (or Lua or Perl or 
whatever) but hard to do in Python.

Since I haven't actually proposed what it would look like in Python, I'm 
not sure that it makes sense to argue that it doesn't "look like" 
Python. That remains to be seen.

>  > The fact that it saves 2 lines of code in many different places in
>  > my program can't easily be illustrated in an email message.
> 
> Well, this far, you haven't even been able to illustrate how your 
> unspecified block syntax would have saved you a single line even in your 
> three-line example.  That's not a good start, really.

Lets try that one again, now that I (hopefully) have given a clearer 
explanation of what I am trying to do.

>> I would disagree with one bit: I think most of those things can be done 
>> in Python today, they are just complicated and ugly under the current 
>> syntax. As much as people decry 'syntactic sugar', I would argue that 
>> those things you mentioned (even the AST example, although it's tricky) 
>> really just need sugar to make them feasible and useful to ordinary 
>> programmers, rather than any fundamental change to the execution model.
> 
> So show us some sugar, then.  After all, Python's design is driven by by 
> an urge to simplify real usage patterns that's been observed in the 
> wild, rather than theoretically-useful-for-everything-maybe language 
> constructs that's been dreamed up in some laboratory.  And all the 
> things I've mentioned *are* used in real code.

Well, I'm not going to give solutions for all of the examples you 
raised, because (a) its off-topic, and (b) it's not necessary. But I 
will take two examples from your list: The LINQ example and the 
RuleDispatch example.

For the LINQ example, all I need to do is point to SQLObject, which does 
  something very similar - it builds an SQL query using Python 
expression syntax. We could improve SQLObjects's syntax quite a bit by 
allowing overloading of the 'and' and 'or' operator - right now, it has 
to use '&' and '|', which have the wrong precedence and require extra 
parens.

For RuleDispatch, we already know it works in Python, the only question 
is how to make it easier to use. And that's been the subject of a number 
of threads on this list, of which there are two parts: How to write the 
dispatcher, and how to express the dispatch constrains in syntax.

Writing the dispatcher isn't a problem - I wrote one for my embedded 
mini-language, Saga, in a day or two just by looking up the 
Chambers/Chen paper on ACM library.

So really it all boils down to syntax.

> Also see the "Patterns are signs of weakness in programming languages" 
> discussions:
> 
>     http://newbabe.pobox.com/~mjd/blog/prog/design-patterns.html

OK I read it - I think the author is overgeneralizing.

> and note that Python's usually aiming for "(nearly) invisible", not 
> "formal", in Norvig's taxonomy.
> 
> Also see the part about use cases for block constructs in my original 
> post to this thread.

-- Talin


More information about the Python-3000 mailing list