Here’s a newbie Elm thing that made me stop and think:
In the docs, there’s a Contracts section, where type annotations are discussed. Some examples like this one are eventually given:
longestName : List String -> Int longestName names = List.maximum (List.map String.length names)
That first line annotates the following function,
longestName, as a function that takes a List of Strings and returns an Int. Pretty straightforward.
Later, when I started looking at the Elm Architecture tutorial, I came across a function with a type annotation that looked like this:
update : Action -> Model -> Model update action model = case action of Increment -> model + 1 Decrement -> model - 1
My brain wasn’t quite ready for
Action -> Model -> Model. The function has two parameters. So why doesn’t the annotation?
Here’s how to think about it: It’s a function that takes an Action and returns a function. The function it returns takes a Model and returns a Model.
Elm functions are defined in “curried form”. That’s what
Action -> Model -> Model is. A function in curried form is expressed as a chain of smaller functions, each of which takes a single argument and returns a function. (Until the last one, which returns the ultimate result.)
Maybe it’s helpful to imagine parentheses here:
Action -> (Model -> Model). Better?
(Action, Model) -> Model. Two parameters, one result.
(“Currying”, by the way, is the process of taking a function with multiple parameters – the kind I’m used to – and transforming it into the chain-of-single-parameter-functions form that Elm uses by default.)
If I call the
update function but provide only the first argument, Elm returns a new function. E.g., at the REPL:
> update Increment <function>: Model -> Model
What does this new function do? It does what the
update function does, without requiring (or allowing) me to pass in an Action. The Action is already known. So this function always takes a Model and returns a new Model with a value of the argument Model’s value plus one. It always increments.
This is called “partial application”. Because I’ve applied only part of the function, bound only one of the parameters. The result is a new function I can use to send in the rest.
(Note: Curried form makes partial application easy. But partial application does not require curried form.)