Wednesday, August 11, 2010

The apply decorator trick

From wikipedia:
In computer science, a closure is a first-class function with free variables that are bound in the lexical environment. Such a function is said to be "closed over" its free variables. A closure is defined within the scope of its free variables, and the extent of those variables is at least as long as the lifetime of the closure itself. The explicit use of closures is associated with functional programming and with languages such as ML and Lisp. Closures are used to implement continuation passing style, and in this manner, hide state. Constructs such as objects and control structures can thus be implemented with closures.
In terms of python you have a closure when a function returns another function. This is something you often do when you use decorators, for example.

However, this is not about decorators. The classical example is the "adder":
>>> def add_creator(n):
...   def adder(m):
...     return m + n
...   return adder
... 
>>> add5 = add_creator(5)
>>> add5(6)
11

This is fun and nice. However, I often use closures in a more functional way to hide state instead of using classes. In this cases you often are not interested in the higher order function.
You use it to hide state in local variables/parameters, but you plan to use the returned function.

E.g.,
def do_something_builder():
    val = very_long_computation(...)
    def do_something(z):
        x = use(val)
        y = do_other_things(z)
        return g(x, y)
do_something = do_something_builder()

In this cases, I use the "apply trick". Apply "calls" the callable argument and
returns the result. apply can be used as a decorator as well.

@apply
def do_something():
    val = very_long_computation(...)
    def do_something(z):
        x = use(val)
        y = do_other_things(z)
        return g(x, y)

This is very similar to schemish things like, which I have seen often:

(define do-something
  (let ([val (very-long-computation)])
     (lambda (z)
       (let ([x (use val)]
             [y (do-other-things z)])
        (g x y)))))

No comments: