Skip to content

CurriedFunctionsImplDecisions

Robert Peszek edited this page Sep 14, 2013 · 2 revisions

Curried Functions Implementation Decisions:

Rejected Ideas:

I decided against extending Groovy Closure classes.
I view these classes as part of Groovy language implementation semi-private details and was afraid that this approach would keep braking with new releases of Groovy. Also, honestly, I prefer to code in Groovy than in Java.

Closure is not very meta-programmable but the decision against meta-programming has more philosophical reasons. Fpiglet is not meta-programming Groovy into something else. Also Fpiglet it trying to show that function composition is a better alternative to meta-programming!

Decided On:

In the selected approach, Groovy closure c is wrapped into a closure which looks more or less like this:

{Object[] args -> callInASpecialWay(c, args) }

I am not going to dwell into implementation details. They are surprisingly not hard.

Currently Fpiglet has 2 classes defining alternative (and equivalent) curried function logic (and looks like I will settle on 'AutoCurring' and not on 'ExplicitCurring' approach). The logic for applying _ syntax has been separated out into 3rd class.
You can find the code in the 'fpig.util.curring' package

From the client program point of view, the simplest way to convert closures is to use static shortcut method called f

import static fpig.common.functions.FpigBase.f

Closure fc = f {a, b ->; ...} //fc is curried function

Note: closures with one logical curried parameter do not benefit from converting. They are curried already!
You can do the following but what for?

Closure fc1 = f {a -> print a}

Brain Teaser side-note:
As described above, since f is defined like so:

f = {c -> return {Object[] args -> callInASpecialWay(c, args)}}

So could just say that

f = {c -> &callInASpecialWay.curry(c)} 

and with curried functions in place you do not need to write explicit curry calls, we only need to convert &callInASpecialWay into a curried function. which is what f is for!

So we could write:

f  = f &callInASpecialWay

which is so much simpler syntax! Duh, it uses curried functions.

If only Groovy compiler understood recursion like that ;)

Design Implications:

  1. Some Typing Loss: Converted closures loose argument type introspection.
    If your code introspects Closure.parameterTypes or Closure.maximumNumberOfParameters this can be an issue.
    I hope few Groovy programs need to do that.

  2. Side-effect on Development Error: Consider closure defined like so:

    def c = f {a-> {b->{c->{d-> a + b + c + d}}}}

and consider what Fpiglet needs to do to execute it like this:

f(1,1,1,1,8)

Fpiglet does not parse the syntax you wrote, it will just see a closure accepting one parameter being called with 5 parameters. It has no choice, but to assume that the result of this call is a closure, to which it will pass next argument.
The process repeats itself until list of passed args is exhausted and whatever final result is returned.
However, in this situation, after partially applying 4-th argument the result is not a closure but 4.

Evaluating 4(8) makes no sense so RuntimeException (actually CurriedFunctionCallException) is thrown.

But wait, the closure already executed!
In pure functional language where there are no side-effects this would be no problem. In Groovy however, closure can do whatever it wants and when exception is raised developer may expect that nothing happened. But it did!

The good news is that such situation should only arise from development errors.
Do not try to handle CurriedFunctionCallException! Fix the code instead.