Road to Elm - Currying, The Unknown

Road to Elm - Table of Contents

If you haven't used FP languages much, you may see many little confusing things happening in Elm code.

For one, you might have found partially applied functions, which are functions that take n arguments but are called with a less than n number of arguments.

Partially Applied Functions

An example straight from the Elm Architecture tutorial #7 is:

(List.map (elementView address) model.gifList)

In this snippet elementView has type Signal.Address Action -> (Int, RandomGif.Model) -> Html.

It takes 2 arguments and returns Html. But we're only giving it 1 argument, a Signal.Address Action.

What's happening here is: any partially applied function (a function that is called with less args than it should) returns a function.

That returned function is made using the arguments that the function call got, evaluating them, and then the function you get takes the remaining number of args.

Example with elm-repl

Let's fire up elm-repl and define addfun : Int -> Int -> Int -> Int:

> addfun x y z = x + y + z
<function> : number -> number -> number -> number

So addfun takes 3 args. Now let's give it 1 arg and save the resulting function in another variable:

> partiallyapplied1 = addfun 2
<function> : number -> number -> number

You can see from the type signature that the number of arguments of the function returned has diminished.

Javascript Equivalent

To do the same thing in Javascript would require quite a bit of wiring:

> var myCurriableFunction = function (x, y, z){
    return function (y, z){
      x + y + z;
    }
  }

> myCurriableFunction(2);

function (y, z){
  x + y + z;
}

And it only supports the 1 arg case. To make a generic solution would require some more code.

Internals

What happens on the inside of the partially applied Elm function is:

> partiallyapplied1 = addfun 2

addfun 2 y z = 2 + y + z -- evaluating the x

addfun y z = 2 + y + z -- the returned function

Going on, applying 1 argument at a time:

> partiallyapplied2 = partiallyapplied1 3
<function> : number -> number

And so on until we finally get to a result which is not a function anymore, but a value.

> partiallyapplied3 = partiallyapplied2 5
10 : number

You can also use any of the intermediate functions we made:

> partiallyapplied1 3 4
9 : number

This in practice mean that you can give to a function in some place, only the arguments you have there. And then you can pass it to another place where you can give it the remaining arguments.

Why it's useful

Taking the function from the tutorial again:

 (List.map (elementView address) model.gifList)

List.map needs a function that takes only one argument. elementView would take 2, so to use it with List.map you need to partially apply it:

elementView : Signal.Address Action -> (Int, RandomGif.Model) -> Html

becomes:

elementView : (Int, RandomGif.Model) -> Html

List.map can now pass each entry in model.gifList which has type: (Int, RandomGif.Model) to the elementView function.

References

These concept comes from untyped lambda calculus, invented by Alonso Church.

It is common, and often fundamental, in functional languages, so it's well worth learning.

I'll probably write about Lambda Calculus in the future, and when I do I'll update this post with a link.

Bye, until next time :)