Elixir has been on my radar for about a year now, however despite my best effort I haven’t had a chance to take a thorough look yet. I decided I’ve been waiting long enough, I just need to sit down and try to hack out some code. If you haven’t looked into the language yet it appears very cool on paper:
Elixir is a functional, concurrent, general-purpose programming language built atop the Erlang Virtual Machine (BEAM). Elixir builds on top of Erlang to provide distributed, fault-tolerant, soft real-time, non-stop applications but also extends it to support metaprogramming with macros and polymorphism via protocols.
I have a little bit of familiarity with Erlang, but besides that this is the first time I’ve actually looked into the Elixir syntax. So the rest of this post will be entirely, unfiltered stream of consciousness.
Trying Out Some Code
Lets see if we can use Elixir to solve the first problem on the Project Euler site.
Multiples of 3 and 5: If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000.
First lets try to fire up the REPL, I think I remember front page mentioning it was called iex.
$ iex Interactive Elixir (1.0.2) - press Ctrl+C to exit (type h() ENTER for help) iex(1)>
Great, so lets try to start working on the problem. Seems like we can naively solve this by traversing a list, lets see if we can figure out the syntax for that. Maybe it’s like Haskell? [1..10.]
iex(1)> [1...1000] ** (SyntaxError) iex:2: syntax error before: '...'
Hrmm… that didn’t work. Maybe lets try dropping the brackets? I think i vaguely remember Erlang only using two dots in the notation?
iex(1)> 1 .. 1000 1..1000
That looks like it could be a representation of a list! I wonder how we could take the head it?
Lets try just a function?
iex(2)> head(1 .. 1000) ** (RuntimeError) undefined function: head/1
Maybe it’s prolog/erlang style, using the symbolic syntax?
iex(3)> x = 1 .. 1000 1..1000 iex(4)> x 1..1000 iex(5)> [y|z] = x ** (MatchError) no match of right hand side value: 1..1000
Looks like that’s not right… something seems off I thought that would work… It seems like the language is probably erlang with some syntax. I vaguely remember the head function in erlang being hd/1, let’s try that on our list.
iex(5)> hd ** (RuntimeError) undefined function: hd/0 iex(5)> hd(x) ** (ArgumentError) argument error :erlang.hd(1..1000) iex(5)> [x] [1..1000] iex(6)> head([x]) ** (RuntimeError) undefined function: head/1 iex(6)> hd([x]) 1..1000 iex(7)> hd([1 .. 2]) 1..2
Hrm… it seems like the 1..1000 syntax doesn’t create a list, maybe it’s a symbol? Time to go read the docs …
So after reading the docs for lists it seems like, the bracket syntax is correct. hd/1 is head as we thought and tl/1b is tail. However they don’t seem to cover the range syntax on that page. Need to dig in to the docs a little bit more I guess, maybe there is no range syntax?
Ahha! It looks like on this page that we were actually kind of correct. 1..1000 seems to represent a lazy range, not a list. So we need to use functions to operate on top of the lazy range.
Looks like there is a Stream package for lazy sequences, which has map/filter:
iex(8)> 1..1000 |> Stream.filter ** (UndefinedFunctionError) undefined function: Stream.filter/1 (elixir) Stream.filter(1..1000)
Need to figure out how to write a lambda, lets try to filter out odd numbers.. one of the map examples in the docs page uses this syntax:
fn x -> x*2 end
Seems to make sense, lets try it out.
iex(8)> 1..1000 |> Stream.filter(fn x -> rem(x,2 == 0) end) ** (ArithmeticError) bad argument in arithmetic expression :erlang.rem(1, false) (elixir) lib/enum.ex:666: anonymous fn/3 in Enum.filter/2 (elixir) lib/range.ex:77: Enumerable.Range.reduce/6 (elixir) lib/enum.ex:666: Enum.filter/2
I would have thought that would have worked… wait the parenthesis don’t look right. I forgot to properly close the rem(..)!
iex(8)> 1..1000 |> Stream.filter(fn x -> rem(x,2) == 0 end) [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, ...]
I think I saw a Enum.sum function as well, putting all the pieces of the question together we get a nice one liner.
iex(12)> multiples = fn x -> rem(x,3) == 0 || rem(x,5) == 0 end #Function<6.90072148/1 in :erl_eval.expr/5> iex(13)> 1..999 |> Stream.filter(multiples) |> Enum.sum <redacted-from-article>
That wasn’t so bad, I like the pipelining syntax. It seems similar to the syntax used in ML based language, like F#/OCaml.
If you are on board with Elixir and want to continue on your own Elixir journey there are a couple of really great resources to continue exploring the language.
- How I Start: Elixir
- Elixir-Lang Getting Started Tutorial
- The 30 Days of Elixir Repository
- Learning X in Y Minutes: Where X = Elixir
- The Elixir Cheat Sheet