Skip to content
Robert Peszek edited this page Sep 14, 2013 · 1 revision

Curried Functions

In Nutshell

Fpiglet Project Home page shows some examples of curried functions. Please review them if you are new to this concept.

Curried Functions are a new concept to Groovy. Curried functions introduced in Fpiglet are very similar to curried functions in Haskell (except Haskell is very strongly typed).

One way to think about Curried Functions is that they are equivalence classes of Closures which do not care about grouping of parameters:

def c1 = {a,b,c,d -> a + b + c + d}

is very much the same as curried function as this

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

or like this

def c3 = {a, b -> {c, d -> a + b + c + d}}

and other similar 'groupings' of parameters.

In fpiglet you can take any of the functions defined above and do this:

import static fpig.common.functions.FpigBase.*

def fc = f c1 //could be c2 or c3 with the same results
assert fc(1)(2)(3)(4) == 10
assert fc(1,2)(3,4)   == 10
assert fc(1)(2,3,4)   == 10
assert fc(1,2,3,4)    == 10
//etc. etc.

Invoking curried function with just a few parameters simply partially applies these parameters.

Using fpiglet, you can even use the following underscore syntax to partially apply closures:

Closure expr = f({a,b,c,d -> a + 2*b + 3*c + 4*d})

//note expr(1,2,_,_) is equivalent to expr(1,2) .. or expr(1)(2)    

Closure needs_a_c = expr(_, 0, _, 0)
assert needs_a_c(1,0) == 1 
assert needs_a_c(0,1) == 3

Notice that, with just using Groovy.curry() it is not possible to make the above closures (c1, c2, c3) behave in identical way.

Also notice that to do that:

assert fc(1)(2)(3)(4) == 10

you would need to do this in Groovy (notice UGLY call() at the end of curry chain):

assert c1.curry(1).curry(2).curry(3).curry(4).call() == 10

Why Curried Functions?

Curried functions are simply more powerful than OO methods.

Consider this Groovy code:

list.inject{acc, el -> (el>acc)? el: acc } 

inject and anonymous Closure passed to inject are all tightly coupled to the list. The 'inject' method is owned by the list.

In Fpiglet code:

reduceL(MAX) << list 

reduceL is first class citizen, it lives independently of data, so does the MAX function. Data and manipulation of data become decoupled.

If I really, really wanted to marry list and reduceL I could simply do this:

reduceL(_, list)

Here is a little more advanced Fpiglet code which relies on curried functions:

import static fpig.funlist.functions.BaseFL.*
import static fpig.groovylist.asfunlist.functions.InAndOutOfFunLists.*

Closure sumAllInGroovyList = withFunList (reduceL(PLUS)) 
//withFunList expects 2 params it is given one, reduceL expects 2 params it is given one
assert sumAllInGroovyList ([2,5,3]) == 10

Many other cool Fpiglet features rely on curried functions heavily.


Wiki page: CurriedFunctionsLogicalLimits

  • describes logical limitations of curried functions in detail

Wiki page: CurriedFunctionsImplDecisions explains

  • implementation decisions
  • implementation limitations.
Clone this wiki locally