Currying (or should we say Schönfinkelning as it was developed by Moses Schönfinkeln and then further developed by Haskell Curry or even Fregeling as Gottlob Frege originally introduced the idea), is technique of translating execution of function of multiple parameters into execution a sequence of functions taking a single parameter (thanks Wikipedia, I couldn’t figure out more succint way of saying that).
Related to currying is concept of partial application. Essentially, when a function is called with fewer parameters than it expects, a new function is returned. This function has captured the parameters supplied in previous call and “remembers” them. When the new function is called and rest of the parameters are supplied, the net effect will be same as if the original function would have been called with all the parameters. It is of course possible to call this new function with less parameters than what it expect, which results to a new function being created.
Toolz is a utility library for Python that among other things makes writing partial functions really easy. It’s possible to use tools provided by Python, namely functools library, for this too, but toolz offers some convenient shortcuts and generally stay out of the way. With functools, it’s the caller’s decision when to create a partial function. With toolz, it’s the called function that automatically detects if some parameters are missing and create a partially applied function.
Documentation of Toolz is pretty good and currying is explained in sufficient detail (I really love when open source project has good documentation). Creating a function that supports currying is as simple as just using curry decorator:
@curry define add(a, b, c): return a + b + c
This is simple enough with Hy too:
(with-decorator curry (defn add [a b c] (+ a b c)))
But what’s the point of all this? Wouldn’t it be easier to just call function with parameters that it needs and be done with it?
One common (for at least for me) use for partial application is different kinds of factory functions. Sometimes you wish to write a general routine (say, for path finding) and then customize it a little bit depending on the specific situation (maybe some characters can move to all directions, while some can move to only 4 cardinal directions). One could have a boolean parameter that would control which version of the search to use. But one possible solution (one that I think is rather elegant) is to pass a function that reports all possible locations reachable from a given location as a parameter to the general path finding function. Place this parameter first in argument list and declare path finding function curried. Now it’s possible to create different kinds of path finding functions by calling the generic one with single parameter. This will cause function to return a new path finding function that uses specified way of finding neighboring cells.
(with-decorator curry (defn a* [adjacent-nodes start goal a-map] ...))
It is possible to achieve similar effect by writing a factory function that takes single parameter, constructs a new function that captures that parameter and then return that. This requires writing an extra function though. And if you wanted to have more configuration options for the constructed function, the factory would soon get pretty complex. Using curry decorator from Toolz library just makes things so much simpler. One could say that automatic currying transforms functions from simple pipelines or processors into powerful machines that solve a general type of problem and can construct specialized machinery for particular problem.
There’s nice little extra trick too. Python has support for named parameters, where order of parameters don’t have to match to the order they were declared in function signature. It’s enough that all needed parameters are specified. Toolz can handle currying with named parameters too. Now developer doesn’t have to spend lots of time trying to figure the order in which they should declare the parameters as the calling code can decide which parameters to supply and in which order. Just make sure to use keyword arguments if the curried function was created by using keyword arguments.
=> (with-decorator curry ... (defn process [foo bar baz] ... ...)) => (setv bar-processor (process :bar 10.0)) => (bar-processor :baz 1 :foo 2)
There’s a drawback of course (there always is, isn’t there?). Python has dynamic typing (depending on the day, it’s either one of the greatest things ever or the most annoying feature keeping me from doing what I want). The gist is that type of an object doesn’t really matter, as long as the object has attribute you currently want to use. Explicit interface definitions don’t really exist in Python, just simple convention will take care of it.
But when I started tinkering with automatic currying, there sometimes were nasty little surprises, where my automated tests didn’t catch errors. I an autocurried function signature was changed by adding a new parameter, all the code that calls that function still worked just fine. However, they received a function object instead of what-ever object they were expecting. Even worse, if the calling code expected a function object to be returned, problem could be even harder to debug.