git / email
index

Curried Elixir: Compose

In a previous post, we implemented a curry function for Elixir, and in the following post, we built a flip function for our curried functions.

I want to build one last thing for our curried function, and that's a composition operator. Elixir doesn't support function composition, you have to use anonymous functions or the capture operator instead:

iex> add_2 = fn x -> x + 2 end
iex> mul_3 = fn x -> x * 3 end
iex> add_2_and_mul_3 = fn x -> mul_3.(add_2.(x)) end # or &mul_3.(add_2.(&1))
iex> add_2_and_mul_3.(5)
21

It's... ok I guess? But I'd really prefer if I could just do something like add_2_and_mul_3 = mul_3 . add_2. . isn't overridable, but we can use any of the custom operators. I'm going to pick <~>.

defmodule Func do
  def f <~> g when is_function(g), do: fn x -> f <~> g.(x) end # Reduce the right-hand function first
  def f <~> x, do: f.(x) # When we're done reducing the right-hand side, reduce the left-hand side
end

Remember, we expect f and g to be curried here, which is why we don't have to worry about their arity: it's always f/1 and g/1.

Let's try this!

iex> add_2 = fn x -> x + 2 end
iex> mul_3 = fn x -> x * 3 end
iex> (mul_3 <~> add_2).(5)
21
iex> flipped_map = (&Enum.map/2) |> curry() |> flip()
iex> flipped_get = (&Map.get/2) |> curry() |> flip()
iex> map_mul_2 = flipped_map.(fn x -> x * 2 end)
iex> f = map_mul_2 <~> flipped_get
iex> a = %{x: [1, 2, 3], y: [100, 200, 300]}
iex> f.(:x).(a)
[2, 4, 6]
iex> g = flip(f).(a)
iex> g.(:x)
[2, 4, 6]
iex> g.(:y)
[200, 400, 600]

With currying, composing and transforming functions becomes a lot easier!

Obviously, Elixir probably isn't the best language for that, as its syntax and the lack of support for curried functions in the language make things look a lot more convoluted than they should.