Falling in love with Elm

I have fallen in love with Elm. I never saw myself as a functional programmer, but having tried a language that literally will not allow you to leave any stone unturned I am converted. So much so that I am seriously considering learning Haskell since it has web frameworks.

You see, as a lone developer, maintainability is paramount and functional languages are much more maintainable. But that is for another day. Today, I want to tell you what I have learned about functions and partial application in elm.

Partial Application

In Elm, an anonymous function is written like so:

\a -> a

This is the identity function. It simply returns whatever parameter (a) is passed to it. If I called it as follows:

(\a -> a) "hello"

then it would return “hello” (realise that this is an anonymous function, which is why it looks a bit odd. Functions which are named by assigning them to a value have a more traditional syntax, i.e. id a = a

If I want to write an anonymous function that takes two arguments I would do it like this:

(\a -> (\b -> a + b))

This anonymous function takes two arguments, a and b and adds them together.

Now, let’s say that I write the following code in elm-repl:

> f = (\a -> (\b -> a + b)) -- Add two numbers together
> f
<function> : number -> number -> number
> g = f 1  -- Partially apply the first argument
<function> : number -> number
> g 2 
3 : number

So in passing only one of two arguments to f we return a new function that has only one argument which assign to g…or do we? Actually, we don’t create a new function. We unwrapped the inner function in f and assign it g. You see, f is actually a function that returns another function. That’s why it has the syntax (arg -> (arg -> result)). The parentheses represent the nesting of one function inside another. When you partially apply an argument to a function you create something that looks awfully like a closure*. g knows the value of a because it is defined in the environment that returned it, but this is invisible to the caller of g.

Using <|

Another way of examining this can be through looking at the <| operator. The <| operator (called the Backward Function Applicator passes the value on its right to the first parameter of the function on its left:

> ((,) <| 1) 2
(1,2) : ( number, number' )

Note that partial application is not really the purpose of <|. See the docs for an explanation (linked above).

Let’s break this down a little.

  • (,) is shorthand for the tuple2 function which has the type \a -> (\b -> (a,b))
  • (,) <| 1 backward applies the number 1 to tuple2 returning the following function: a -> (number, a). Note that we have gone from (a,b) to (number, b). So the value of a in the returned function has been filled in**. It now just needs its second argument to complete the computation.
  • ((,) <| 1) 2 passes 2 to (a -> (number, a) which returns (1,2).

So we see that <| unwraps a nested function from an outer function by calling the outer function with the second argument given to |>. The inner function is returned with the argument provided to the outer function assigned wherever it is going to be used by the inner function.

Aha!

The type annotation for <| is: (a -> b) -> a -> b. This provided the ‘aha’ moment for me about how partial application works. It says:

“I take two parameters: a function that accepts a and returns b, and an instance of a. I will return an instance of b.”

Now what is an instance of b? Well, if you use <| where (a -> b) is a function that only takes one argument you get the result of calling (a -> b) with an instance of a, i.e.

> f = (\a -> a + 1)
<function> : number -> number
> f <| 1
2 : number

But if you use <| where the first argument is a function that has two arguments then a partially applied function is returned! In other words the b in (a -> b) is the inner function in (\a -> (\b -> a + b)) or similar i.e.:

> f = (\a -> (\c -> a + c))
<function> : number -> number -> number
> f <| 1
<function> : number -> number -- this is (\c -> 1 + c)

Recall that the type annotation for <| is (a -> b) -> a -> b

I hope this is helpful. Please feel to make any comments or suggest improvements below.

* I’m not saying it is a closure, since a quick Google search didn’t find a definitive answer to this point but if anyone happens to know I’d love to find out!
** This is why I suspect that it does not, in fact create a closure but something similar.

Written with StackEdit.

Comments

Popular Posts