Stratus3D

Software Engineering, Web Development and 3D Graphics

Currying and Partial Application in Elixir

I often forget what currying and partial application are and how they are used, so I am writing this blog post to solidify these concepts in my memory.

Currying

Currying is when a single function with multiple arguments is converted into multiple functions, each expecting one of the arguments of the original function. For example, we could curry the :erlang.atom_to_binary/2 function in Elixir like this:

> binary_fn = fn(encoding) ->
>     fn(atom) ->
>         :erlang.atom_to_binary(atom, encoding)
>     end
> end
#Function<6.80484245 in :erl_eval.expr/5>

> binary_fn.(:utf8).(:bar)
"bar"

We now have a function that takes the first argument, and returns another function that expects the second. This is currying. As you can imagine there are many possible uses for this. Including partial application…

Partial Application

Partial application is when you curry a function and pass fewer arguments than the original function expected. Continuing with our atom_to_binary example, this would be partial application:

> atom_to_utf8 = binary_fn.(:utf8)
#Function<6.80484245 in :erl_eval.expr/5>

We now have provide one of the two arguments required to invoke atom_to_binary/2. We receive a new function which we store in atom_to_utf8 variable. This function expects the second argument of atom_to_binary/2 to be passed in. When this function is invoked, it calls atom_to_binary/2 with the values :utf8 (which was passed in earlier) and :foo.

> atom_to_utf8.(:foo)
"foo"

This is partial application. Partial application relies on currying to produce functions that contain references to the arguments that have been provided(via closures) and expect the remaining arguments of the original function.

Currying allows us to break a function down into many functions, and partial application uses currying to create functions with some of the arguments already applied. Both of these concepts are extremely useful. We can, as we did above, create new functions on the fly by providing a subset of the arguments, and we can also pass around the curried functions, providing the arguments they require as those arguments become available. There are quite a few possible uses for currying. I may write another post about currying and closures in Elixir sometime in the future but this is all for today.