Curried functions

Robert Brewer fumanchu at amor.org
Sun Jun 6 15:54:22 EDT 2004


Holger Türk wrote:
> a common pattern to write curried functions (i.e. without the
> need for a special partial application syntax, like in PEP 309)
> is like in this example:
> 
> def functionLock (lk):
>      def transform (fn):
>          def lockedApply (*argl, **argd):
>              lk.acquire ()
>              try:
>                  fn (*argl, **argd)
>              finally:
>                  lk.release ()
>          return lockedApply
>      return transform
> 
> PEP 318 (decorators) contains some other examples.
> I wonder if someone else than me would appreciate a syntax
> like this:
> 
> def functionLock (lk) (fn) (*argl, **argd):
>      lk.acquire ()
>      try:
>          fn (*argl, **argd)
>      finally:
>          lk.release ()
> 
> It would even enable us to write curried function definitions
> like this one:
> 
> def f (*a) (x=1, **b) (*c, **d):
>      [...]
> 
> Current approaches about currying or partial application
> can't handle the latter in a simple/transparent way.
> 
> Will it be hard to implement the feature?
> Are there any reasons that this syntax/concept is a bad idea?

I realize you're demonstrating by example, but I can't see why the first block wouldn't be written:

def functionLock (lk, fn):
    def lockedApply (*argl, **argd):
        lk.acquire ()
        try:
            fn (*argl, **argd)
        finally:
            lk.release ()
    return lockedApply

...that is, I can't recall a case where I've doubly-nested a function like your example.

Anyway, a def like:

def f (*a) (x=1, **b) (*c, **d):
     [...]

seems...limiting, I guess. The developer of 'f' doesn't always know exactly which arguments consumers are going to want to partially apply, in which order or groupings. A more generic approach would alleviate (if not completely remove) the need to nest. I'd rather see a consumer-side solution to partial application, where your example function is defined straightforwardly:

def functionLock(lk, fn, *argl, **argd):
    lk.acquire()
    try:
        fn(*argl, **argd)
    finally:
        lk.release()

and the consumer decides when to supply arguments. They could decide to write:

f_lock = partial(functionLock, lk)
for func in func_set:
    f_lock(func, 1, 2, 3)

or their purposes might be better served by applying two args "up front":

locked_f = partial(functionLock, lk, fn)
for i in range(10):
    locked_f(i, i + 1)

It depends on the context.


Robert Brewer
MIS
Amor Ministries
fumanchu at amor.org




More information about the Python-list mailing list