How DWiki uses partial function evaluation
As part of a longer entry, Muharem Hrnjadovic asks:
I would love to see examples or code snippets that are made possible and/or improved greatly by leveraging
I'm not sure that my example qualifies, but I'll take a shot at it.
DWiki has a WSGI-like processing pipeline to handle requests; they get passed from function to function, possibly getting mutated on the way down and possibly having the results mutated on the way back up.
The pipeline functions look like this:
def TimeLayer(next, request): t0 = time.time() resp = next(request) td = time.time() - t0 request.log("Time: %.3g sec" % td) return resp
next is called: there is no hypothetical
parameter. This means that there's a disconnect between how these
functions want to be called and how they want to call the next step in
the pipeline (and how the outside world wants to start the pipeline);
they want to know the next function to call, but they don't want to have
to know the next function's next function (and so on down the line).
The answer is partial function evaluation. All of the
and the function called at the top of the entire pipeline, are partially
evaluated functions with the
next parameter filled in in the process
of building the pipeline.
DWiki predates Python 2.5 and the functools module, so
it currently uses a closure and lambda-based approach. However,
functools.partial is clearly the better way to write the function that
assembles the pipeline; it would come out roughly like this:
def genDwikiStack(stklst): # The bottom function takes no # next argument. cur = stklst.pop() while stklst: nxt = stklst.pop() cur = functools.partial(nxt, cur) return cur
(This can also be written as a
reduce one-liner, assuming you
have more than one function in
stklst and you change it so that
stklst is bottom to top instead of top to bottom.)
One of the small issues with DWiki's current lambda-based approach is that stack backtraces are somewhat confusing and overly verbose, since they alternate real functions with the lambdas that wrap them. One potential advantage of a real implementation of partial function evaluation is avoiding this, although I suspect that the current implementation doesn't.