Securing a future for anonymous functions in Python

P

Paul Rubin

Nick Coghlan said:
Anyway, I'm looking for feedback on a def-based syntax that came up in
a recent c.l.p discussion:

Looks like just an even more contorted version of lambda. It doesn't
fix lambda's main deficiency which is inability to have several
statements in the anonymous function.
 
N

Nick Coghlan

Paul said:
Looks like just an even more contorted version of lambda. It doesn't
fix lambda's main deficiency which is inability to have several
statements in the anonymous function.

Do you consider generator expressions or list comprehensions deficient because
they don't allow several statements in the body of the for loop?

Cheers,
Nick.
 
J

John Roth

Nick Coghlan said:
GvR has commented that he want to get rid of the lambda keyword for Python
3.0. Getting rid of lambda seems like a worthy goal, but I'd prefer to see
it dropped in favour of a different syntax, rather than completely losing
the ability to have anonymous functions.

Anyway, I'm looking for feedback on a def-based syntax that came up in a
recent c.l.p discussion:
http://boredomandlaziness.skystorm.net/2004/12/anonymous-functions-in-python.html

Cheers,
Nick.

I think it's rather baroque, and I agree with Paul Ruben
that it needs multiple statement capability.

The syntax I prefer (and I don't know if it's actually been
suggested before) is to use braces, that is { and }.

In other words, an anonymous function looks like:
{p1, p2, p3 |
stmt1
stmt2
}

There are two reasons for using braces. One is
that it's the common syntax for blocks in a large number
of languages. The other is that it should be relatively
easy to disambiguate from dictionary literals, which are
the only other current use of braces.

The parameter list is optional, as is the bar ending
the list. The reason for the bar instead of a colon
is to help the parser in the case of a single parameter,
which would look like the beginning of a dictionary
literal. If the parser doesn't need the help, then a colon
would be more consistent, hence better.

A second issue is indentation. I'd set the indentation
boundary wherever the _second_ line of the construct
starts, as long as it's to the right of the prior
indentation boundary. The unfortunate part of this, and one of the
major stumbling blocks, is that it might take some
significant reconceptualizing of the lexer and parser,
which currently isn't set up to shift from expression
back to statement mode and then return to expression
mode.

John Roth
 
I

Ian Bicking

John said:
The syntax I prefer (and I don't know if it's actually been
suggested before) is to use braces, that is { and }.

In other words, an anonymous function looks like:
{p1, p2, p3 |
stmt1
stmt2
}

What's the advantage of something like that over the non-anonymous
equivalent:

def some_func(p1, p2, p3):
stmt1
stmt2

I appreciate some of the motivation, but merely avoiding giving
something a name doesn't seem like a laudible goal.

The one motivation I can see for function expressions is
callback-oriented programming, like:

get_web_page(url,
when_retrieved={page |
give_page_to_other_object(munge_page(page))})

The problem with the normal function in this case is the order of
statements is reversed:

def when_retrieved_callback(page):
give_page_to_other_object(munge_page(page))
get_web_page(url, when_retrieved=when_retrieved_callback)

Oh, and you have to type the name twice, which is annoying. For most
other functional programming constructs, list (and not generator)
comprehensions work well enough, and the overhead of naming functions
just isn't that big a deal.

I think this specific use case -- defining callbacks -- should be
addressed, rather than proposing a solution to something that isn't
necessary. Which is to say, no one *needs* anonymous functions; people
may need things which anonymous functions provide, but maybe there's
other ways to provide the same thing. Decorator abuse, for instance ;)

def get_web_page_decorator(url):
def decorator(func):
return get_web_page(url, when_retrieved=func)
return decorator

@get_web_page_decorator(url)
def when_retrieved(page):
give_page_to_other_object(munge_page(page))

Or even (given a partial function as defined in some PEP, the number of
which I don't remember):

@partial(get_web_page, url)
def when_retrieved(page):
give_page_to_other_object(munge_page(page))

It's okay not to like this proposal, I don't think I'm really serious.
But I do think there's other ways to approach this. Function
expressions could get really out of hand, IMHO, and could easily lead to
twenty-line "expressions". That's aesthetically incompatible with
Python source, IMHO.
 
R

Roy Smith

Ian Bicking said:
I think this specific use case -- defining callbacks -- should be
addressed, rather than proposing a solution to something that isn't
necessary. Which is to say, no one *needs* anonymous functions; people
may need things which anonymous functions provide, but maybe there's
other ways to provide the same thing. Decorator abuse, for instance ;)

I'm not a big functional programming fan, so it should not come as a
surprise that I don't often use lambda. The one place I do use it is in
unit tests, where assertRaises() requires a callable. If what you're
testing is an expression, you need to wrap it in a lambda.

I suppose you could call this a special case of a callback.
 
S

Skip Montanaro

John> In other words, an anonymous function looks like:
John> {p1, p2, p3 |
John> stmt1
John> stmt2
John> }

John> There are two reasons for using braces. One is that it's the
John> common syntax for blocks in a large number of languages.

Yeah, but it's not how blocks are spelled in Python. As Nick pointed out on
his blog, allowing statements within expressions risks making code more
difficult to read and understand.

People keep trying to make Python something it is not. It is not
fundamentally an expression-only language like Lisp, nor is it an
expression-equals-statement language like C. There are good reasons why
Guido chose the relationship between simple statements, compound statements
and expressions that he did, readability and error avoidance being key.

Skip
 
C

Carl Banks

Nick said:
GvR has commented that he want to get rid of the lambda keyword for Python 3.0.
Getting rid of lambda seems like a worthy goal, but I'd prefer to see it dropped
in favour of a different syntax, rather than completely losing the ability to
have anonymous functions.

I shall either coin or reuse a new term here: "premature optimization
of the Python language."

(Please note that the word premature is not intended to be an accurate
description, but irony by analogy, so spare me any semantic
nitpicking.)

In much the same way that programmers often spend a lot of time
optimizing parts of their program that will yield very minor dividends,
while they could have spent that time working on other things that will
pay off a lot, many of the wannabe language designers here are spending
a lot of time on aspects of the language for which any improvement
would only pay small dividends.

I think the worry about anonymous functions is one of the most
widespread cases of "premature optimization of the Python Language."
One could argue about the various benefits of particular choices, maybe
even make a convincing case that one is best in accord with the design
goals of Python; but in the end, the divends are small compared to
improving other aspects of the language.
 
D

David Bolen

Ian Bicking said:
The one motivation I can see for function expressions is
callback-oriented programming, like:

get_web_page(url,
when_retrieved={page |
give_page_to_other_object(munge_page(page))})

This is my primary use case for lambda's nowadays as well - typically
just to provide a way to convert the input to a callback into a call
to some other routine. I do a lot of Twisted stuff, whose deferred
objects make heavy use of single parameter callbacks, and often you
just want to call the next method in sequence, with some minor change
(or to ignore) the last result.

So for example, an asynchronous sequence of operations might be like:

d = some_deferred_function()
d.addCallback(lambda x: next_function())
d.addCallback(lambda blah: third_function(otherargs, blah))
d.addCallback(lambda x: last_function())

which to me is more readable (in terms of seeing the sequence of
operations being performed in their proper order), then something like:

def cb_next(x):
return next_function()
def cb_third(blah, otherargs):
return third_function(otherargs, blah)
def cb_last(x):
return last_function()

d = some_deferred_function()
d.addCallback(cb_next)
d.addCallback(cb_third, otherargs)
d.addCallback(cb_next)

which has an extra layer of naming (the callback functions), and
requires more effort to follow the flow of what is really just a simple
sequence of three functions being called.
I think this specific use case -- defining callbacks -- should be
addressed, rather than proposing a solution to something that isn't
necessary. (...)

I'd be interested in this approach too, especially if it made it simpler
to handle simple manipulation of callback arguments (e.g., since I often
ignore a successful prior result in a callback in order to just move on
to the next function in sequence).

-- David
 
B

Bengt Richter

GvR has commented that he want to get rid of the lambda keyword for Python 3.0.
Getting rid of lambda seems like a worthy goal, but I'd prefer to see it dropped
in favour of a different syntax, rather than completely losing the ability to
have anonymous functions.

Anyway, I'm looking for feedback on a def-based syntax that came up in a recent
c.l.p discussion:
http://boredomandlaziness.skystorm.net/2004/12/anonymous-functions-in-python.html
Nit: You didn't try the code you posted ;-)
>>> funcs = [(lambda x: x + i) for i in range(10)]
>>>
>>> def incrementors():
... for i in range(10):
... def incrementor(x):
... return x + i
... yield incrementor
... ...
9 9 9 9 9 9 9 9 9 9 ...
9 9 9 9 9 9 9 9 9 9

This is an easy trap to fall into, so if the new lambda-substitute could
provide a prettier current-closure-variable-value capture than passing a dummy default
value or nesting another def and passing the value in, to provide a private closure for each,
that might be something to think about.

IMO an anonymous def that exactly duplicates ordinary def except for leaving out
the function name and having a local indentation context would maximize flexibility
and also re-use of compiler code. People could abuse it, but that's already true
of many Python features.

From your web page:
----
def either(condition, true_case, false_case):
if condition:
return true_case()
else:
return false_case()

print either(A == B, (def "A equals B"), (def "A does not equal B"))
either(thefile, (def thefile.close()), (def 0))
----

I'd rather see :)something) than (def something) for this special case,
but the full-fledged anonymous def would spell it thus:

print either(A == B, (def():return "A equals B"), (def(): return "A does not equal B"))
either(thefile, (def(): return thefile.close()), (def(): return 0))

BTW,

funcs = [(lambda x: x + i) for i in range(10)]

might be spelled

funcs = [(def(x, i=i): return x + i) for i in range(10)]

or optionally

funcs = [(
def(x, i=i):
return x + i
) for i in range(10)]

or

funcs = [(def(x, i=i):
return x + i) for i in range(10)]
or
funcs = [(def(x, i=i):
return x + i)
for i in range(10)]
or
funcs = [def(x, i=i):
return x + i
for i in range(10)]
or
funcs = [
def(x, i=i):
return x + i
for i in range(10)]

and so on. (the def defines the indentation base if the suite is indented, and the closing ')'
terminates the anonymous def explicitly, or a dedent to the level of the def or less can do it,
as in the last two examples).

This one

(def f(a) + g(b) - h(c) from (a, b, c))

would be spelled (if I undestand your example)

(def(a, b, c): return f(a)+g(b)+h(c))

which seems to me familiar and easy to understand.

BTW, there are old threads where this and other formats were discussed. I am still
partial to the full anonymous def with nesting indentation rules. Syntactic sugar
could be provided for useful abbreviations (that could possibly be expanded by the
tokenizer -- re which possibilities I haven't seen any discussion than my own
recent post, BTW), but I'd like the full capability.

Regards,
Bengt Richter
 
J

John Roth

Ian Bicking said:
What's the advantage of something like that over the non-anonymous
equivalent:

def some_func(p1, p2, p3):
stmt1
stmt2

I appreciate some of the motivation, but merely avoiding giving something
a name doesn't seem like a laudible goal.

Actually, it is a laudable goal. It's always easier to understand
something when it's right in front of your face than if it's
off somewhere else.

This, of course, trades off with two other forces: avoiding
repetition and making the whole thing small enough to
understand.

So the niche is small, single use functions. The problem
with lambdas is that they're restricted to one expression,
which is too small.

Languages that are designed with anonymous functions
in mind use them very heavily. Smalltalk is the standard
example, and it's also one of the major (possibly the
only) attraction Ruby has over Python.

Python isn't designed that way, which restricts their
utility to a much smaller niche than otherwise.
The one motivation I can see for function expressions is callback-oriented
programming ...

Well, that's true, but that's a very global statement:
when you pass a function into another routine, it's
essentially a callback.

....
Function expressions could get really out of hand, IMHO, and could easily
lead to twenty-line "expressions". That's aesthetically incompatible with
Python source, IMHO.

Anything can get out of hand; there's no way of legislating
good style without restricting the language so much that it
becomes unusable for anything really interesting. Even then
it doesn't work: see COBOL as a really good example of
good intentions gone seriously wrong.

Have you ever programmed in a language that does use
anonymous functions extensively like Smalltalk?

John Roth
 
J

Jeff Shannon

David said:
I'd be interested in this approach too, especially if it made it simpler
to handle simple manipulation of callback arguments (e.g., since I often
ignore a successful prior result in a callback in order to just move on
to the next function in sequence).

It seems to me that what most people *actually* want, when asking for
lambdas, is a quicker and more convenient way to get closures. (At
least, that's what the vast majority of examples of lambda use seem to
be for.) Perhaps one could get a bit more traction by looking for
improved closure syntax instead of focusing on the anonymous function
aspect.

All of the suggestions for anonymous multiline functions (with embedded
indentation) seem to me to miss what seems to me to be the only
significant benefit of lambda -- its ability to be used in-line without
creating a huge ugly tangle. (I'd argue that lambdas create a *small*
ugly tangle, but apparently that's just me. ;) ) Lambdas do have some
value in defining callbacks, but that value derives almost exclusively
from the fact that they are in-line (rather than a few lines above).
Mimicking function-def indentation inside of another function's arglist
strikes me as an abomination just waiting to happen; in comparison, the
need to type a name twice seems trivial.

As a result, it seems to me that, rather than generalize lambdas into
"full" anonymous functions (with most of the negatives and few of the
positives of lambda), it would be much better to specialize them further
into inline-closure-creators, where they can serve a valuable purpose
without quite as much risk of code pollution.

Jeff Shannon
Technician/Programmer
Credit International
 
B

Bengt Richter

It seems to me that what most people *actually* want, when asking for
lambdas, is a quicker and more convenient way to get closures. (At
least, that's what the vast majority of examples of lambda use seem to
be for.) Perhaps one could get a bit more traction by looking for
improved closure syntax instead of focusing on the anonymous function
aspect.
Not sure what you mean by closure here. To me it means the necessary
external environment needed to be captured for use by a function definition
getting exported from its definition environment. I.e., it is something
a function uses, and part of the function definition, but it isn't the
function itself. I would compare a closure more to a callable class instance's
self attributes, except that the latter are more flexible.

In fact, for a callback, a constructor call creating a suitable
callable class instance could sometimes work well as a substitute
for a lambda expression, ISTM. (I.e., when it is not important to
show the code in line, and the differences are in initialization parameters
rather than code).
All of the suggestions for anonymous multiline functions (with embedded
indentation) seem to me to miss what seems to me to be the only
significant benefit of lambda -- its ability to be used in-line without
creating a huge ugly tangle. (I'd argue that lambdas create a *small*
ugly tangle, but apparently that's just me. ;) ) Lambdas do have some
value in defining callbacks, but that value derives almost exclusively
from the fact that they are in-line (rather than a few lines above).
They do let you define _code_ inline, which a constructor call doesn't do
(unless you pass a string to compile etc -- not cool).
Mimicking function-def indentation inside of another function's arglist
strikes me as an abomination just waiting to happen; in comparison, the
need to type a name twice seems trivial.
Self-restraint can avoid abominations ;-)
As a result, it seems to me that, rather than generalize lambdas into
"full" anonymous functions (with most of the negatives and few of the
positives of lambda), it would be much better to specialize them further
into inline-closure-creators, where they can serve a valuable purpose
without quite as much risk of code pollution.

There's always the temptation to be enforcer when being persuader
is not the easiest ;-)

(BTW, again, by closure, do you really mean deferred-action-thingie?)

Regards,
Bengt Richter
 
J

Jeff Shannon

Bengt said:
Self-restraint can avoid abominations ;-)

It can, but given the prevalence of lambda abominations (such as the
many that the Martellibot described among Cookbook submissions), is
there any reason to believe that there would be *more* restraint in
using a less-constrained feature?? :)
(BTW, again, by closure, do you really mean deferred-action-thingie?)

My understanding of "closure" (which may well be wrong, as I've
inferred it entirely from past discussions on this newsgroup) is that
it's a callable with some or all of its parameters already set; e.g.,

def one_and(foo):
def closure(arg):
return foo(1, arg)
return closure

incr = one_and(operator.add)

The function one_and() returns a closure, here bound to incr. It is
essentially a (partially) deferred action thingy, if you want to use
technical terms. ;) I suppose that one could look at it as the
environment in which to call a given function, exported for later use...

My thesis here is that one of the most common (legitimate) uses of
lambda is as an adapter, to create an intermediary that allows a
callable with a given signature to be used in places where a different
signature is expected -- that is, altering the number or order of
arguments passed to a given callable (and possibly also capturing the
current value of some other variable in the process). I feel that
it's more fruitful to focus on this "adapter" quality rather than
focusing on the "anonymous function" quality.

Jeff Shannon
Technician/Programmer
Credit International
 
N

Nick Coghlan

Bengt said:
This is an easy trap to fall into, so if the new lambda-substitute could
provide a prettier current-closure-variable-value capture than passing a dummy default
value or nesting another def and passing the value in, to provide a private closure for each,
that might be something to think about.

I forgot about that little trap. . .

Cheers,
Nick.
 
N

Nick Coghlan

Carl said:
Nick Coghlan wrote:
In much the same way that programmers often spend a lot of time
optimizing parts of their program that will yield very minor dividends,
while they could have spent that time working on other things that will
pay off a lot, many of the wannabe language designers here are spending
a lot of time on aspects of the language for which any improvement
would only pay small dividends.

Whereas I see it as wannabe language designers only being able to tinker at the
edges of Python, because GvR already got the bulk of the language right.
Anonymous functions are the major core construct that doesn't 'fit right', so a
disproportionate amount of time is spent playing with ideas about them.

Cheers,
Nick.
 
B

Bengt Richter

It can, but given the prevalence of lambda abominations (such as the
many that the Martellibot described among Cookbook submissions), is
there any reason to believe that there would be *more* restraint in
using a less-constrained feature?? :)
Maybe. If people are determined to overcome limitations that needn't exist,
they are likely to invent horrible hacks ;-)
My understanding of "closure" (which may well be wrong, as I've
inferred it entirely from past discussions on this newsgroup) is that
it's a callable with some or all of its parameters already set; e.g.,

def one_and(foo):
def closure(arg):
return foo(1, arg)
return closure

incr = one_and(operator.add)

The function one_and() returns a closure, here bound to incr. It is
essentially a (partially) deferred action thingy, if you want to use
technical terms. ;) I suppose that one could look at it as the
environment in which to call a given function, exported for later use...

Quoting from "Essentials of Programming Languages" (Friedman, Wand, Haynes):
(using *xxx yyy etc* for italics)
"""
In order for a procedure to retain the bindings that its free variables had
at the time it was created, it must be a *closed* package, independent of
the environment in which it is used. Such a package is called a closure.
In order to be self-contained, a closure must contain the procedure body,
the list of formal parameters, and the bindings of its free variables.
It is convenient to store the entire creation environment, rather than
just the bindings of the free variables. We sometimes say the procedure
*is closed over* or *closed in* its creation environment. We represent
closures as records.
"""

So it looks like you infer better than I remember, and attachment to
faulty memory has led me to resist better inferences ;-/

Closure is the name for the whole thing, apparently, not just the environment
the procedure body needs, which was the aspect that I (mis)attached the name to.

(Representing closures as records doesn't really belong in that paragraph IMO,
since it is not really part of the definition there, just a choice in that stage
of exposition in the book, using scheme-oriented examples. But I quoted verbatim.).

On the subject CLtL finally (after some stuff beyond my current caffeine level) says,
"""
The distinction between closures and other kinds of functions is somewhat pointless,
actually, since Common Lisp defines no particular representation for closures and
no way to distinguish between closures and non-closure functions. All that matters
is that the rules of lexical scoping be obeyed.
"""
I guess that clinches it ;-)

Might be a good python glossary wiki entry.
My thesis here is that one of the most common (legitimate) uses of
lambda is as an adapter, to create an intermediary that allows a
callable with a given signature to be used in places where a different
signature is expected -- that is, altering the number or order of
arguments passed to a given callable (and possibly also capturing the
current value of some other variable in the process). I feel that
it's more fruitful to focus on this "adapter" quality rather than
focusing on the "anonymous function" quality.
I see what you are saying (I think), but I think I'd still like a full
anonymous def, whatever adapter you come up with. And I prefer to be persuaded ;-)

Regards,
Bengt Richter
 
S

Steven Bethard

Jeff said:
My thesis here is that one of the most common (legitimate) uses of
lambda is as an adapter, to create an intermediary that allows a
callable with a given signature to be used in places where a different
signature is expected -- that is, altering the number or order of
arguments passed to a given callable (and possibly also capturing the
current value of some other variable in the process). I feel that it's
more fruitful to focus on this "adapter" quality rather than focusing on
the "anonymous function" quality.

Maybe the 'functional' module proposed in PEP 309[1] could provide such
functions?

py> def ignoreargs(func, nargs, *kwd_names):
.... def _(*args, **kwds):
.... args = args[nargs:]
.... kwds = dict((k, kwds[k])
.... for k in kwds if k not in kwd_names)
.... return func(*args, **kwds)
.... return _
....
py> def f(x, y):
.... print x, y
....
py> ignoreargs(f, 2)(1, 2, 3, 4)
3 4
py> ignoreargs(f, 2, 'a', 'b')(1, 2, 3, 4, a=35, b=64)
3 4

Steve

[1] http://python.fyxm.net/peps/pep-0309.html
 
P

Paul Rubin

print either(A == B, (def "A equals B"), (def "A does not equal B"))
either(thefile, (def thefile.close()), (def 0))

I'd really rather have some reasonable macro facility, than to resort
to using anonymous functions and deferred evaluation for common things
like that.
 
I

Ian Bicking

John said:
Actually, it is a laudable goal. It's always easier to understand
something when it's right in front of your face than if it's
off somewhere else.

Naming the function doesn't move it far away. It changes the order (you
have to define it before you use it), and it introduces a name.
Well, that's true, but that's a very global statement:
when you pass a function into another routine, it's
essentially a callback.

Sure, technically. But I'm thinking of real use cases. One I'm
familiar with is things like map and filter. These are generally better
handled with list expressions (and MUCH more readable as such, IMHO).
Another is control structures, ala Ruby or Smalltalk. IMHO, we have all
the control structures we need -- while, for, if. Most "novel" control
structures in Ruby or Smalltalk are just another take on iterators. The
exception being callbacks, and perhaps some other lazy evaluation
situations (though outside of what I think of as "callbacks", I can't
think of any lazy evaluation situations off the top of my head).

So that's why I think callbacks are important; callbacks in the style of
Twisted Deferred, GUI events, etc.
Anything can get out of hand; there's no way of legislating
good style without restricting the language so much that it
becomes unusable for anything really interesting. Even then
it doesn't work: see COBOL as a really good example of
good intentions gone seriously wrong.

OK, I should go further -- even a two-line expression (i.e., an
expression that contains meaningful vertical whitespace) is
aesthetically incompatible with Python source. Which covers any
anonymous function that is more powerful than lambda. I'm not arguing
that it can be abused, but more that it isn't any good even when it's
not being abused.
Have you ever programmed in a language that does use
anonymous functions extensively like Smalltalk?

Yep, I've done a fair amount of Smalltalk and Scheme programming. I
don't expect Python to act like them. I appreciate the motivation, but
I don't think their solution is the right one for Python.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top