[Python-ideas] Proposal for function expressions (original) (raw)

Chris Perkins chrisperkins99 at gmail.com
Sun Jul 12 22:11:20 CEST 2009


I have a proposal for a language feature - a limited form of function- definition expression, similar (superficially) to Ruby's blocks.

The big problem with making "def" an expression is the indentation of the function body. You can't just embed an indented block of code into the middle of an expression. The solution I propose (inspired by Ruby) is to allow a block of code to be appended to the end of an expression in certain, limited circumstances. The block is simply syntactic sugar for a local anonymous function definition which is passed as an argument to the function call that it follows.

First, a simple example to give the broad strokes: foo() do: BODY

is equivalent to:

def ANON(): BODY foo(ANON)

where the name ANON is a placeholder for illustration - it is never actually bound in the local namespace.

Specifically, here is what I propose:

1) In the following, foo recieves TWO arguments: 23 and a callable,

in that order. foo(23) do: pass

2) Here, foo also recieves TWO arguments: a callable and 23, in that

order. foo(&, 23) do: pass

Why "&"? No particular reason - just because Ruby uses "&" for something similar (but not the same). Why do we need the "&" feature? Consider this example:

map(&, my_list) do(item): return do_something_to(item)

This also works for keyword arguments:

foo(a=3, b=&, c=7) do(): whatever()

To make this syntax work, we need several restrictions on where a block is allowed. Intuitively, the rules are:

To clarify, the first rule means that this is not allowed: if foo() do: # are we in the body of the block, or of the if?

The second rule means that this is not allowed: bar(23, foo() do: body_of_block() ) # closing brace of call to bar

Here are some properties of blocks that may not be immediately obvious:

OK, let's move on to the fun part: Motivating Examples.

###################

Networking

dfr = twisted.whatever(...) dfr.addCallback() do(result): handle(result) dfr.addErrback() do(err): handle_err(err)

###################

GUI (for some hypothetical library)

b = my_widget.add_button('Go') b.on('click') do(evt): if evt.ctrl_key: do_something() else: do_other_stuff()

###################

Routing HTTP requests

map.match('/') do(req): return render_home(req) map.match('/about') do(req): return render_about(req) map.match(/(?P\w+)/$/) do(req, **kw): return handlerskw['controller']

###################

Threads

thread.start_new() do: do_some_work()

###################

Reduce the temptation to cram too much into a lambda.

Sort a list of filenames that look like this:

Xyz-1.1.3.tgz, acbd-4.7.tgz, Xyz-1.2.5.tgz, ...

my_list.sort(key=&, reverse=True) do(item): name, _ = os.path.splitext(item) root, _, version = name.partition('-') parts = version.split('.') return (root.lower(),) + tuple(map(int, parts))

stuff = filter(&, my_list) do(item): # some code here... return pred(item)

stuff = re.sub(rx, &, s) do(m): t = m.group(0) if cond(t): return t.upper() else if othercond(t): return t.lower() else: return ''

###################

Faking switch

switch(expr) do(case): case(int) do: stuff() case(str, bytes) do: other_stuff()

switch(value) do(case): case[0:10] do(val): handle_small(val) case[10:100] do(val): handle_medium(val) case.default() do(val): handle_big(val)

###################

Overloaded functions (adapted from PEP 3124)

@overload def flatten(when): when(basestring) do(ob): yield ob when(Iterable) do(ob): for o in ob: for ob in flatten(o): yield ob when.otherwise() do(ob): yield ob

later:

flatten.when(file) do(f): for line in f: yield line

###################

Sort-of a "with(lock)" block, but running the body in a thread.

pool.run_with(my_lock) do: # lock is held here work() other_work() # in parallel

###################

Well, that should give you the general idea.

Some final random points:

Implementation is here: http://bitbucket.org/grammati/python-trunk/ Patch is here: http://bugs.python.org/issue6469 To be honest, I'm not entirely sure if the patch is in the right format - it comes from hg, not svn.

Chris Perkins



More information about the Python-ideas mailing list