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 :)
Comments? Give me a shout at @lambda_cat.
To get the latest post updates subscribe to the LambdaCat newsletter.
You can support my writing on LambdaCat's Patreon.