Sean Eshbaugh

Web Developer + Programmer

Recursive Closures in Elixir

File this under "things that are obvious in retrospect". I got to wondering if it's possible to recursively call a closure in Elixir. The answer is of course, yes, but with a small caveat. You simply have to pass the function (so it can't be truly anonymous) as an argument so it can be used as a callback.

Consider the following pathetic first attempt:

lol = fn
  0 -> 0
  x -> x + lol.(x - 1)

IO.puts lol.(100)

Looks good, right? Not quite...

seshbaugh ~% elixir lol.exs
** (CompileError) /Users/seshbaugh/lol.exs:3: function lol/0 undefined
    src/elixir_translator.erl:463: :elixir_translator.translate_each/2
    src/elixir_translator.erl:613: :elixir_translator.translate_arg/2
    lists.erl:1339: :lists.mapfoldl/3
    lists.erl:1340: :lists.mapfoldl/3
    src/elixir_translator.erl:620: :elixir_translator.translate_args/2
    src/elixir_translator.erl:80: :elixir_translator.translate_each/2
    lists.erl:1339: :lists.mapfoldl/3

The closure has no idea what we're talking about. Which shouldn't really come as a surprise, we're still in the middle of defining the value of lol (that's my understanding as of right now, if I discover that I'm right for the wrong reason I will be sure to update this post). If we want to call lol from within itself we have to pass a reference to it to itself, like so:

lol = fn
  _, 0 -> 0
  lol, x -> x + lol.(lol, x - 1)

IO.puts lol.(lol, 100)

This time around we pass lol has an argument to itself. In the first function clause the callback is ignored (since its purpose is to halt the recursion) and in the second one we use call it, with itself as the first argument again. Now when we run this we get the expected output:

seshbaugh ~% elixir lol.exs

I'm not sure if this is really all that useful, since you could (and probably should) use defp in your modules to define private functions that aren't available outside the module. But it never hurts to know what's possible!

More Elixir Adventures

Last night I went to a surprisingly crowded meetup featuring Dave Thomas (of "pickaxe" fame) giving a talk about Elixir. I had a great time hearing him share his experience and the chance to pick his brain about Elixir was rather exciting. His most interesting take away point: Elixir may or may not be the next "big thing" in programming languages, but whatever it ends up being, it's going to look and feel very similar to Elixir. I think I'm starting to be inclined to agree.

The best part about the meetup was that I left with a better understanding of Elixir's (and Erlang's) concept of pattern matching and why it's so important. About a week ago I was attempting to do Project Euler problem 1 in Elixir. Whenever I encounter a new language I like to re-solve some of the easier Euler problems with it just to get a feel for the language, how to set it up an environment, and how to handle the basic syntax and patterns. Since I've already done most of them in Ruby or JavaScript I have a basic understanding of what I'm trying to do. My first attempt at Euler #1 is, in retrospect, embarrassing. When Dave Thomas said that early on with Elixir he was stuck in a Ruby mindset I totally understood what he meant.

defmodule Euler do
  def naive_euler_1(x) do
    if x <= 1 do
      if rem(x, 3) == 0 || rem(x, 5) == 0 do
        x + naive_euler_1(x - 1)
        naive_euler_1(x - 1)

IO.puts Euler.naive_euler_1(999)

I suppose it's not terrible considering I had only read and understood a few parts of the "Getting Started" documentation. At least it's recursive!

After getting home last night I immediately set out to re-write the above but using the awe-inspiring power of pattern matching.

defmodule Euler do
  def better_euler_1(x) when x <= 1, do: 0
  def better_euler_1(x) when rem(x, 3) == 0 or rem(x, 5) == 0, do: x + better_euler_1(x - 1)
  def better_euler_1(x), do: better_euler_1(x - 1)

Holy crap. No conditionals. Okay, well, technically there are conditionals, but they're in the form of guard statements on the pattern matching part of the function clause (I really hope I'm getting my terms right here!). Still, this is much cleaner and, from what I understand, much more idiomatic. Dave was rather emphatic in pointing out that by removing the standard if-then-else type of conditional logic you remove the possibility of many of the bugs typically found in software. I can't say I know from experience that that's true, but it's a very intriguing argument and one I hope to explore.

After Dave was done speaking a couple of people from the audience went up front and shared some things they've been doing with Elixir lately. The first person, while giving a quick rundown of his version of Conway's Game of Life pointed out something interesting that had only been briefly touched upon earlier while Dave was speaking (although I'd seen it before while reading up on Elixir); the |> macro. |> takes whatever is returned from the left and passes it as the first argument to a function on the right. It's a simple but powerful expression. Realizing this I set out to shorten my solution to Euler #1 even further.

IO.puts 1..999 |> Enum.filter(fn x -> rem(x, 3) == 0 || rem(x, 5) == 0 end) |> Enum.reduce(0, fn (x, sum) -> x + sum end)

Again, holy crap. This is almost identical to my standard Ruby solution.

puts (1..999).select { |i| i % 3 == 0 || i % 5 == 0 }.inject(:+)

In fact, were I reading it out loud and explaining it to someone I would probably end up using almost the same English words in both cases. To me, that's a good sign.

As soon as I'm done writing this I'm going to pre-order Dave's new book on Elixir. He said he's as excited about Elixir as he was about Ruby many years ago. Color me excited too.

I heard you like Ruby and Erlang so I put Ruby inside Erlang for you

Now I know I'm only just scratching the surface of this whole Elixir thing, but I have a sneaking suspicion I'm going to be feeling lots of deja vu...

~% irb
2.0.0p247 :001 > name = "world"
 => "world" 
2.0.0p247 :002 > "Hello, #{name}!"
 => "Hello, world!" 
2.0.0p247 :003 > 

~% iex
Erlang R16B01 (erts-5.10.2) [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Interactive Elixir (0.10.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> name = "world"
iex(2)> "Hello, #{name}!"
"Hello, world!"