[Python-ideas] Ruby-style Blocks in Python Idea

Stephen J. Turnbull stephen at xemacs.org
Wed Mar 11 06:05:28 CET 2009

Guido van Rossum writes:

 > I'm feeling really dense right now -- I still don't see the difference
 > between the two. Are you saying that you would prefer an expression
 > that creates a *named* function? That seems to be really bizarre --
 > like claiming that you don't like expressions that return anonymous
 > numbers.

Here's a use-case from Emacs.  Various modes have callbacks so that
users can customize them.  A typical case is that a text-mode hook
will turn on auto-fill-mode, which is documented as an example to be
done like this:

(add-hook 'text-mode-hook (lambda () (auto-fill-mode 1)))

where *-mode functions called with nil toggle the mode, positive
numbers turn it on, and non-positive numbers turn it off.  The
add-hook function is supposed to be idempotent: it won't add the same
hook function if it is already in the hook.  The problem is if you
change the lambda and execute that form, the changed lambda is now not
identical to the lambda on the hook, so the old version won't be
removed.  This

(add-hook 'text-mode-hook (defun turn-on-auto-fill () (auto-fill-mode 1)))

neatly avoids the problem by returning the name of the function, the
symbol `turn-on-auto-fill', which is callable and so suitable for
hanging on the hook.  If you change the definition and execute the
above form, add-hook *mutates nothing* (the symbol is already
present), but because the hook is indirect through the function's
symbol and the defun *is* executed, the definition changes ... which
is exactly what you want.[1]

AMK's use-case could be post-processed as something like

(let ((i 0))
  (mapcar (lambda ()
            (let ((name (intern (format "foo-%d-callback" i))))
              (define-function name (aref slots i))
              (aset slots i name)
              (setq i (1+ i))

where slots is the vector of anonymous functions.  Providing names in
this way costs one indirection per callback invocation in Emacs Lisp,
but the benefits in readability of tracebacks are large, especially
for compiled code.

 > I don't see the conceptual difference between a "def-expression" (if
 > it were syntactically possible) and a lambda-expression. What is the
 > difference in your view? Are you sure that difference exists? (It
 > wouldn't be the first time that people ascribe powers to lambda that
 > it doesn't have. :-)

AIUI, a def-expression binds a callable to an object, while a lambda
expression returns a callable.  An anonymous def is just lambda by a
different name (and I think the code block proponents agree, based on
their willingness to accept syntax using def instead of lambda).

I don't see how the kind of thing exemplified above would be useful in
Python, and from a parallel reply I just saw, I gather Jim agrees.
The point is to show how a function-defining expression can be useful
in some contexts.  This works in Emacs Lisp because in

(setq foo (lambda ...))

tools (including the Lisp interpreter) will not recognize foo as a
function identifier although its value is a function, while

(defun foo ...)

marks foo as a function identifier.  But in Python (like Scheme)
they're basically the same operation, with a little syntactic sugar.
Anything that is based on the separation of variable namespace from
function namespace is DOA, right?

The renaming mapper is a different issue, I think; it depends on
computing object names at runtime (ie, the Lisp `intern' operation),
not on separate namespaces.  I'm not sure offhand how to do that in
Python, or even it it's possible; I've never wanted it.

[1]  N.B.  Of course modern Emacsen define turn-on-auto-fill as a
standard function.  But this is ugly (because of the single flat
namespace of Emacs Lisp), and not all modes have their turn-on,
turn-off variants.  auto-fill-mode was chosen because (a) the
semantics are easy to imagine and (b) the use of lambda in a hook is
explained by exactly this example in the Emacs Lisp Manual.

More information about the Python-ideas mailing list