Elixir is a new language we are building that targets the Erlang VM. Based on our strong Ruby background, we were asked several times and finally decided to write a blog post about why Rubists should play with Elixir. Granted that some of Elixir’s syntax and features comes from Ruby, however Elixir is definitely closer to Erlang than anything else. Single assignment variables, immutability, easy communication between processes, lists, tuples, binaries and OTP behaviors are all available in Elixir. Therefore, you will be able to learn a lot by using Elixir, here are the things I have enjoyed the most learning:
Pattern matching is one of the features I enjoy the most in functional programming. They provide an easy to extract information from a data structure. For instance, in Elixir, you would write:
[first, second] = [1,2] first % => 1 second % => 2
You could definitely write something similar using Ruby assignment operator, but the gain in pattern matching comes from the fact you can use them in method signatures and, if a method signature does not match one specific pattern, the next method is tried. For instance, here is a method that iterates through a list of strings and print them:
module Printer % This pattern matches a list with at least one element. % The first element of the list is assigned to h, the remaining % is assigned to tail. def print([head|tail]) IO.puts head print(tail) % Recursively calls print with the tail end % Eventually the list will be empty and the method above % won't match, causing this one to match instead. def print() % Do nothing, we are done printing items end end Printer.print ["foo", "bar", "baz"]
Another cool example is a recursive method that checks if a list is a prefix of another list:
module Prefix % Iterate both lists. Check if the first element is equal then call itself again. % If the first element (i) is not equal, it won't match this method. def is?([i|prefix], [i|list]) is?(prefix, list); end % If the prefix is empty, is because everything matched, so return true. def is?(, _list) true; end % Else, return false def is?(_prefix, _list); false; end end Prefix.is?([1,2,3], [1,2,3,4,5]) % => true Prefix.is?([0,1,2], [1,2,3,4,5]) % => false
Finally, pattern matching allows us to easily have key-value args, one of the features planned to Ruby 2.0. This examples comes straight from Elixir code (notice that symbols in Elixir start with a single quote instead of colon):
module Record def retrieve(name, 'from: file) % Implementation ... end end
The fact that neither Elixir nor Erlang provides conditional loops like while forces you to think differently and use pattern matching in different occasions. Thinking differently about a problem that you are frequently solving in Ruby and coming up with a completely different solution is in general a very insightful and enjoying process.
Also, notice that both Elixir and Erlang do tail call optimization, so the methods above will perform nicely while you could get huge stack traces if you implemented such methods in Ruby in a similar fashion.
Single assignment variables and immutability
What I haven’t showed in the example above is that variables in Erlang are single assignment, therefore, the following will raise an error:
[first, second] = [1,2] first = 10
The above raises a bad match error because, since a value was already assigned to first, the second expressions
first = 10 is actually comparing 1 to 10, which obviously fails. The fact variables are assigned just once is what allows us to implement our prefix method above, ensuring the value assigned to i matches in both lists.
Also, both in Elixir and Erlang, objects/data structures cannot be modified in place. They are immutable. Every modification generates a new object/data structure. Immutability removes the shared state internal to the language (you still have shared state if you have to do external operations) and play a key role on Erlang’s and Elixir’s concurrency.
Personally, I have found immutability to be a blessing and a curse at the same time. Mutability is not commonly an issue when working with Rails applications, but while working on Rails itself and other gems, we need to be frequently thinking about mutability. Sometimes you pass an object (like a hash or an array) to a method that modifies it in place and then you have to spend some time tracking how that new element ended up in your object.
When working in Elixir, since everything is immutable, I don’t need to worry about such cases and yes, it feels good! It feels like going from C to Java and then I suddenly don’t need to worry about memory management. However, such benefit comes with an obvious downside that some code just becomes more verbose.
To show this downside, consider a possible create action for PostsController similar to Rails, but where we have rewritten it to consider both immutability and single assignment variables:
def create post0 = Post.new(params[:post]) post1 = post0.set(:published, false) post2 = post1.save if post2.persisted? redirect_to post2 else render :new end end
In such cases, the ORM could likely provided better APIs, but that is a pattern that would repeat over and over again. For instance, modifying the response cookies would require an explicit change in the response object:
response1 = response.cookies.set "tracker_code", "123456"
In any case, just working with a language that provides immutability and single assignment variables is worth the experience. You will learn a lot by trying it out that I could ever mention in a blog post. As pattern matching, it frequently forces you to handle problems through a different perspective which is extremely challenging and rewarding.
UPDATE: As some pointed out in the comments, Elixir can remove single assignment and still play properly with Erlang’s foundations. For this reason, Elixir now allows you to assign to the same variable more than once and has more flexible rules related to the variable scope (similar to Ruby) when compared to Erlang.
Communication between processes
Pattern matching, single assignment variables and immutability provides a good foundation for communication between processes. If you are interested to learn more about this one and see all these things working together, watch the screencast from previous week!
A different Object Model
As I mentioned in the screencast and in the README, contributing to Elixir is quite easy because both the Standard Library and tests are written in Elixir itself. Now, imagine that you had to implement something like Ruby’s Set in Elixir. You would not learn about Ruby’s Set more, but also think about the best structure to use, which algorithm would be the best to update the Set and what would be a good API to access those objects.
Besides, there are other basic tools that are required in any language, like packaging system, documentation parser and test libraries that are currently missing in Elixir and you could be the one to implement it!
I hope I have convinced you to try out Elixir. You can learn much more about the language itself by checking out its README.
Finally, if you need some inspiration to do crazy things, I recommend the Forking Ruby talk by Dave Thomas. As he said, we love Ruby and we are not moving away from it anytime soon, but forking it or trying out new things is a great way to improve the language and its ecosystem, and I hope Elixir helps you think more about that.
English. You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.