Time for another random syntax idea. . .

So, I was tinkering in the interactive interpreter, and came up with the following one-size-fits-most default argument hack:

Py> x = 1
Py> def _build_used():
...   y = x + 1
...   return x, y
...
Py> def f(_used = _build_used()):
...   x, y = _used
...   print x, y
...
Py> f()
1 2
Py> x = 3
Py> f()
1 2

Works pretty well in terms of getting early binding, compile-time evaluation of expensive values and psuedo-constants, initialising a local with a shadowed value from an outer scope, sharing mutable values between invocations, and well, really, anything that default argument hacks are used for. It has the benefit of allowing use of a full suite at compile time, instead of the expressions one is usually limited to in default arguments hacks. It also slightly improves the argspec pollution situation by only using one argument instead of several.

However, it's still ugly as sin, still pollutes the functions argspec, the lists of names in the assignment statement and the return statement have to be kept in sync manually, and you're now polluting the outer namespace as well. Not to mention the fact that the contents of the compile-time functions are miles away from where the results are used.

But consider a syntax like the following:

    def f():
        use x, y from:
            y = x + 1  # [1]
        print x, y

[1] I'll grant that the binding of x from the outer scope here is more than a little obscure. However, I could see 'use x from: pass' becoming an idiom for early binding, in which case the example 'use' block could be written:
use y from: y = x + 1
use x from: pass


Then mixing early binding with early evaluation in the same 'use' block might simply be considered bad style, and discouraged (although not prevented).

Essentially, the function is compiled as usual, and emits code at the location of the 'use' statement equivalent to that for "x, y = <const>". The relevant entry in co_consts is populated by executing the body of the 'use' statement with an implicit "return x, y" at the end. The environment for that execution is the same as that for any function defined at the same level as the containing scope of the 'use' statement (e.g. module level in the example).

Multiple 'use' blocks would be allowed in a scope. A 'use' block at module level would simply mean that the result of calling the block gets marshalled into the compiled module file, rather than the block itself.

You could get performance improvements on *any* function, simply by moving code which doesn't depend on the functions arguments inside a 'use' block. For modules, data structures initialised inside a using block could simply be unmarshalled rather than built anew.

Cheers,
Nick.

P.S. I didn't search the archive, because I couldn't figure out any search terms for the topic that weren't swamped by irrelevant hits.

--
Nick Coghlan   |   [EMAIL PROTECTED]   |   Brisbane, Australia
---------------------------------------------------------------
            http://boredomandlaziness.skystorm.net
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to