rubyday 2015

Elixir for the rubyist

Elixir for the rubyist

by Hal Fulton

In his talk "Elixir for the Rubyist" presented at RubyDay 2015 in Turin, Hal Fulton explores the programming language Elixir, emphasizing its compatibility with Erlang and its elegant syntax that may appeal more to Ruby developers. Fulton opens with a humorous introduction about his fondness for Italy and reminisces about the significance of Italian words in both food and music. He transitions to discussing the strengths and weaknesses of Ruby, highlighting that while it is object-oriented and user-friendly, it struggles with concurrency and is sometimes considered slow.

Fulton contrasts Ruby with Erlang, which excels in concurrency due to its lightweight processes and robust virtual machine (BEAM). He notes that although Erlang is powerful, its syntax can be perceived as unattractive and carries some deprecated legacy features.

Key Points Discussed:

  • Strengths of Ruby: Highly object-oriented, beautiful syntax, user-friendly, but poor concurrency handling.
  • Concerns with Ruby: Not ideal for handling large numbers of threads and is sometimes labeled as "slow" depending on the context of its use.
  • Introduction to Erlang: Highlights its focus on concurrency and availability, managed through lightweight processes; however, its syntax could deter some developers.
  • Transition to Elixir: Elixir builds on the BEAM and OTP frameworks, offering a syntax that's more appealing for Rubyists while maintaining full compatibility with Erlang.
  • Innovative Features of Elixir: Fulton's discussion includes the definition of anonymous functions, a pipeline operator that streamlines function chaining, and the functionality to define multiple function heads to reduce complexity.
    • Example Program: He provides an illustrative example of an Elixir program that searches for anagrams in a dictionary, demonstrating the use of functions, data structures, and enumerations.
  • Macros and Declarative Style: Emphasizes how Elixir's features promote a more declarative coding style, reducing unnecessary complexity through smart recursion and multiple function definitions.
  • Vision for the Future of Computing: Fulton reflects on the potential for future programming styles that fuse functional and object-oriented paradigms, suggesting that advancements in multiprocessing may lead to more effective systems in programming.

Main Takeaways:

  • Elixir is a promising language that caters to those who appreciate Ruby’s syntax but need the concurrency strengths of Erlang.
  • By connecting the two worlds of Ruby and Erlang, Elixir can facilitate powerful programming practices with delightful syntax.
  • The future of programming could see a shift towards more integrated approaches that leverage both functional and object-oriented principles, ultimately enhancing application performance and development capabilities.
00:00:13.160 It's a great pleasure to be in Turin. I've been to Italy before, but it was a long time ago when I was much younger. The last time I was here, many of you were in negative numbers, but at least they weren't imaginary numbers, right? One of my great memories from my first visit to Italy was the street signs. Every American knows certain Italian words, mostly for food, which is very good. However, if you studied music, like I did, you also know Italian for the musical terms. I've been seeing those terms since the age of nine. I remember my first experience in Italy: I saw a sign that said 'adagio', which I thought was hilarious because I only knew it as a musical term. I expected to see further signs that said 'allegro ma non troppo' or 'passante marcato,' but I didn't spot any of those signs. Anyway, it's a pleasure to be here. I'm in Turin for the first time, so my life is now complete. Thank you! This talk is about Elixir, and I think there are some of you who already know something about it. If you do, you probably won't learn anything today, but if you've never seen it before or heard of it, maybe you will learn something new. Let me proceed.
00:01:00.000 I think everybody knows why we use Ruby. We like it because it's highly object-oriented, has beautiful syntax, and is easy to use. It just fits the way our brains work for most of us. However, I think Ruby is not very good for concurrency. I had a friend who tried to start more than a thousand threads in Ruby, and he found that some of them would just silently die. While Ruby can handle cooperating processes, its threading model and concurrency in general are not very good. Additionally, some people say Ruby is slow. I believe it depends on what you are using it for. Some people say Ruby is 'adagio'; I say it's 'allegro ma non troppo'. How many of you are familiar with Erlang? Some of you? Good! I was first exposed to Erlang at a Ruby conference in 2006 or 2007, where someone gave a short presentation on it that I found fascinating. I started trying to learn more about Erlang, but I quickly found that, despite its power, it didn't fit my brain. It had all kinds of jagged edges that stuck out of my skull when I tried to cram it inside.
00:02:11.000 Erlang is a functional language and is highly concurrent, making it very good for concurrency. The processes in Erlang are small, fast, and lightweight. It's important to understand that the processes we're talking about are not operating system processes; they are small processes created and managed by the Erlang VM. You can easily start a hundred thousand of them without really noticing, and they run concurrently, all managed by the VM. The BEAM is the Erlang Abstract Machine, and every Erlang program is compiled down to BEAM bytecodes, similar to Java. There's also something called the OTP, which originally stood for Open Telecom Platform, but now it has little to do with telecom. It's a rich set of libraries for process management and many other things, and it has also been time-tested.
00:03:06.000 One thing that makes Erlang special is its capability for concurrency and high availability. There's a wonderful quote from Tim Bray of Sun Microsystems, given in a talk at OSCON in 2008. He stated that if someone paid him a lot of money to build a large-scale message handling system, he would unhesitatingly choose Erlang to build it. I think that speaks highly of Erlang. So, the next question is: why not Erlang? My first answer would be its ugly syntax. This is a subjective opinion; people who've been using Erlang for a long time may get angry when you say it's ugly—it's like saying their child is ugly! But, we're all Rubyists here, so I can say it. There are inconsistencies in Erlang. Every piece of human technology, whether it's a programming language or something else, has inconsistencies because it grows based on needs and not all designed in advance. Additionally, there are things that seemed more relevant 25 years ago than they do now.
00:04:18.000 I also call some of the legacy issues in Erlang 'onions in the varnish'. I stole this phrase from Paul Graham, who you may know. Does anyone recognize the name Primo Levi? [Pause for audience response.] Levi, a chemist at a company that made paints and varnishes, told an interesting story. He found that an onion was in the recipe, but nobody knew why it was there. It turned out that many years ago, they didn’t have good temperature equipment, so they would throw an onion into the mixture and once it fried, they knew it was hot enough. Years later, people were adding an onion to the varnish recipe for no reason at all. I think we do that in many areas of life, including computer science.
00:05:16.000 So, my next question is: why Elixir? This builds on the question of why we should use Erlang. Elixir runs on the same virtual machine as Erlang and is fully compatible with it. You can call Erlang code from Elixir and vice versa; they're both compiled down to the same bytecodes. You can also call OTP from Elixir, which has prettier syntax as well. In addition, Elixir has more modern language features and borrows a lot from Ruby syntax and semantics, making it more palatable for those of us who already think in Ruby.
00:06:21.000 In summary, Elixir borrows from both Erlang and Ruby, but it also innovates. There are features in Elixir that are neither from Erlang nor from Ruby. I hesitate to say any features are truly new, as all good ideas have likely been thought of before. Every time I think I’ve seen a new idea, I find that it was actually taken from someone who thought of it decades ago. I would characterize the relationship between these languages in this way: Erlang is like a lion—it means business and gets things done. It's very powerful, but can also be intimidating. Ruby, on the other hand, is like a beautiful, intelligent woman that you want to spend time with.
00:07:30.000 When you combine a lion and a beautiful woman, you create a sphinx. There are two kinds of sphinxes in the ancient world, and I chose this particular kind because it has wings. The wings represent the innovations in Elixir that don't come from Ruby or Erlang. Let’s start with how we define anonymous functions in Elixir. The first notation is 'fn' for function and 'end', with the code in between. You call the anonymous function by specifying the variable followed by a dot and parentheses. This distinguishes it from a named function.
00:08:26.000 There’s also a prettier syntax for anonymous functions in Elixir, where we say 'take two parameters and add them', and this is defined with 'ampersand' and the parentheses. Inside the function, the parameters are referred to by number, making it very intuitive and simple, and both syntaxes work the same way. By the way, some people don't like the dot-parentheses notation, but it's necessary in some cases when you want to refer to a function without calling it since parentheses are optional. Elixir has a feature called the pipeline operator, which reminds me of the UNIX pipe. This allows you to access a database, get a set of customers, get a bunch of orders for those customers, figure out the tax, and prepare the income tax filing. We've all written similar code, but it tends to use unnecessary temporary variables.
00:09:45.000 In Elixir, instead of this convoluted approach, we can compose all the functions together using the pipeline operator, which passes the left-hand side as the first parameter to the right-hand side, omitting the parameter. This means we can take the customers, pipe that into the function that creates the orders, then compute sales tax and prepare the tax filing. It makes our code look more like an assembly line processing everything step by step.
00:10:56.000 Now, let me show you an actual example of a program in Elixir that searches for anagrams in a dictionary. In the program, we define a module called AnagramFinder, inside of which we define several functions. The first function reads a file list of words, one per line. It uses 'get_words', defined as private (denoted by 'defp'), to read the complete file content and split it into an array with one item per element.
00:11:57.000 From the 'search_file' function, we pass the result into 'process_all'. The 'process_all' function takes the list of words and uses 'Enum.group_by'—which is similar to 'Enumerable' in Ruby. This function allows us to work with collections. We pass an empty data structure and a function that sorts the characters in each word to find anagrams. The sorted characters will serve as the bucket name for the anagrams.
00:13:05.000 Next, we call 'anagram_each', which functions like an iterator. Although we don't traditionally think in terms of loops in Elixir, it works fine. We're passing the function 'print_words' and using slash one at the end to indicate that it's a function with one parameter. This allows us to differentiate functions with the same name but different numbers of parameters. In 'print_words', we address how to handle the outputs based on the data structure.
00:14:11.000 If the parameter looks like a tuple consisting of a key and a list of words, we do nothing if the list has only one item, as that word has no anagrams. In the usual case, we pass in a tuple containing the key and a list of words. We use a lisp-like notation to split off the head of the list and perform actions on that. We then use 'put_s' to take the first word as the label and show the other words separated by commas.
00:15:09.000 There are additional interesting features in Elixir. Multiple function heads and guard clauses allow us to describe parameters for proper function dispatch. Elixir even has powerful macros, defined in a module called 'my_macros', which can create new syntax that behaves like standard constructs. Passing a test and a certain expression into the macro allows us to evaluate conditions in a more convenient manner.
00:16:25.000 You might have encountered an interesting method called 'unless', where a test is evaluated for truth or false and the related expression is executed accordingly. An interesting feature of macros is that they don’t evaluate their parameters immediately; they wait until later. This allows for some flexibility when expressing logic without immediately executing it.
00:17:50.000 Elixir helps reduce looping and branching in code through its features, promoting a more declarative style over the imperative approach, thereby reducing complexity. There's a well-known saying in English about giving someone enough rope to hang themselves—meaning that sometimes people can make mistakes with too much freedom in coding.
00:18:56.000 In Elixir, we can define multiple function heads to avoid branches and utilize smart recursion to avoid loops. Elixir is also tail recursive, ensuring that these operations are handled optimally. I wish to take a moment to mention the future of multiprocessing. The very early days of object-oriented programming back in 1967, as envisioned by Alan Kay, focused on the potential of highly concurrent systems.
00:20:26.000 Kay envisioned objects as being asynchronous and able to communicate with each other. I think we are moving toward a fusion of functional and object-oriented programming styles. You might want to explore the concept of the connection machine, which had 65,000 processors back in the 80s. Though it didn't prove practical at the time, I believe solutions like it will come to fruition in the future. Imagine a machine that isn't just limited to our current 8 or 16 cores!