July 13, 2015

Currying is a technique that translates a function of arity N into a sequence of functions that all have the arity 1. Function arity means the number of arguments a function takes.

# The add function has the arity of 2 add = fn x, y -> x + y end

The term curry/currying was coined back in the 60s referring to the logician Haskell Curry. The programming language named after him has curried functions built in, and many other languages have implemented the same kind of behavior via libraries.

Implementing currying in Elixir #

Elixir is a functional language, but unlike Haskell, it does not have built in function currying.

In Haskell we could do this

let add x y = x + y -- add is curried by default let increment = add 1 increment 3 -- returns 4

A naive approach #

Elixir functions are first class, so your function can return them and receive them as values.

add = fn x -> fn y -> x + y end end

And our add function can be used like the Haskell version

increment = add.(1) increment.(3) # => returns 4

This works, but its obvious that we want something more general, and reusable. In fact the above could be considered a plain function that is partially applied.

We can do better #

Again, Elixir being a fantastic language has tools we can use to make a more general solution. To get the job done we will use pattern matching, and recursion.

In the first paragraphs we talked about arity. Arity is very important in Elixir and Erlang. The language actually considers functions with the same name, but with different arity completely different functions. This is why you see a slash followed by an integer e.g. some_function/2 in various places in the language documentation.

Lets build our own curry function #

The first function our curry implementation needs is the starting point, here our function has the arity of 1.

The :erlang atom lets you call Erlang code (calls are zero cost), here we gather information about the function that is to be curried, using pattern matching we only care about the second returned value, omitting the first with _ so the compiler wont yell at us for unused variables. Finally we call the curry function again, but this time with a different arity.

def curry(fun) do {_, arity} = :erlang.fun_info(fun, :arity) curry(fun, arity, []) end

This time our curry function has the arity of 3. We receive the function that is to be curried, the function arity and arguments. The arguments is just a empty list in the first call.

This function will return a function, taking an argument arg and recurse on itself decrementing the variable arity by one. The arity variable is often called an accumulator, and is widely used in recursive functions. Finally the last argument that was originally an empty list will be filled with the supplied argument.

def curry(fun, arity, arguments) do fn arg -> curry(fun, arity - 1, [arg | arguments]) end end

Stop! Wasn’t functions in Elixir supposed to distinguished by name and arity? So how can Elixir distinguish two functions with the same name and the same arity?

To find out we can test how Elixir calls functions

defmodule Callme do def im_unique {:unique, "callme"} do IO.puts "Ring ring" end def im_unique {:unique, "callyou"} do IO.puts "No answer" end end Callme.im_unique {:unique, "callme"} # => Ring ring

Both functions have the same name, same arity and even accepts the same type of input. Still there a small difference, the tuple is not the same. So we can conclude that pattern matching is used in function invocation.

Lets build the final piece of our curry function.

When the second curry function has recursed as many times as the original function that we wanted to curry had as arity the pattern no longer matches, and Elixir will match the last curry function. The arity parameter is now down to 0 and we can apply the original function to the arguments.

Because we have recursed the parameters we will need to reverse the list so we dont mess up the original order of the parameters the user has passed in. The fun function will have the same arity as the number of items collected in the arguments list.

def curry(fun, 0, arguments) do apply(fun, Enum.reverse arguments) end

Putting it all together #

Lets have the code in one place, and test it out!

defmodule Curry do def curry(fun) do {_, arity} = :erlang.fun_info(fun, :arity) curry(fun, arity, []) end def curry(fun, 0, arguments) do apply(fun, Enum.reverse arguments) end def curry(fun, arity, arguments) do fn arg -> curry(fun, arity - 1, [arg | arguments]) end end end

Lets see how to use our curry function.

import Curry curried = curry(fn a, b, c, d -> a * b + div(c, d) end) # => #Function<0.122306914/1 in Curry.curry/3> # We get a function back! # Lets apply some params! curried.(5).(5).(10).(2) # => 30 # We get a result when all params are passed! five_squared = curried.(5).(5) five_squared_plus_five = five_squared.(10).(2) # => 30 # We get a result!

A few more examples #

By currying functions you can build small reusable functions that are really simple to test, and reason about.

defmodule Curried do import Curry def match term do curry(fn what -> (Regex.match?(term, what)) end) end def filter f do curry(fn list -> Enum.filter(list, f) end) end def replace what do curry(fn replacement, word -> Regex.replace(what, word, replacement) end) end end

# Match when there is a space has_spaces = Curried.match(~r/\s+/) # Reuse has_spaces in our filter sentences = Curried.filter(has_spaces) # Censor the bad things disallowed = Curried.replace(~r/[jruesbtni]/) # Insert censor token censored = disallowed.("*") # Pass the arguments to the curried function allowed = sentences.(["justin bibier", "and sentences", "are", "allowed"]) # => returns ["justin bibier", "and sentences"] # Pipe the first sentence and apply the censor allowed |> List.first |> censored.() # => returns "****** ******"

As you see currying makes it very simple to reuse functions, and build up higher order functions. In Elixir its even more elegant than in most other languages because you can use the pipe operator to chain functions.

549 Kudos