Ryan Findley

Object Oriented Thinking with Elixir and OTP

Object Oriented Thinking with Elixir and OTP by Ryan Findley

Processes in Erlang / Elixir resemble objects in many ways. Some even argue that Erlang processes and the Actor Model are a purer form of object-orientation. The Elixir community has a large contingent of Rubyists that have extended many of the core values (and joys) of Ruby into the world of Elixir. This talk exposes some of the reasons why while providing a starting point for further learning

GoRuCo 2017

00:00:16.690 Hello, my name is Ryan Findley. I've been primarily a Ruby developer for a long time now, and I'm here to talk to you about the Elixir programming language today. But first, I want to give you a bit of perspective on where I'm coming from.
00:00:29.330 At work, we focus on projects that last for many years, not just a few months. Therefore, I am hesitant to adopt tools that may not stand the test of time. We have many active projects that require strong standards and norms from the communities of the tools we choose to utilize. You could say that I'm conservative about the tools I recommend for our customers. I've been dabbling in Erlang for nearly as long as I've been using Ruby, but it never quite fit my problems as well as Ruby did. Even though I had friends who fell in love with Erlang, I never gained much momentum with it.
00:01:01.670 OTP is a collection of middleware and libraries in Erlang, and using it is how you effectively get things done in Erlang. Erlang was originally developed at the Ericson Computer Science Laboratory in the 1980s to build telephone switches. Interestingly, OTP stands for Open Telecom Platform, which may seem less relevant today. Erlang, in many ways, is the foundation upon which Elixir is built. You can think of Elixir as being to Erlang what Scala or Clojure is to Java, if Java were already a functional language. Elixir has a syntax that compiles down to the Erlang Virtual Machine, and you can freely use your Erlang code within your Elixir code. From my perspective, inheriting the stability of the Erlang platform provides a solid foundation and that's what initially attracted me.
00:01:40.490 As a Rubyist in the Elixir community, it feels a bit like being in an expat bar. Not everyone has come from the same background, but everyone is basically speaking the same language. They might sound a little different, but they have their own mantras that can scratch an itch if you've been in the Ruby and Rails world for a while. For example, while we say 'Don't repeat yourself,' they say 'No magic,' and in my mind, those two principles balance each other.
00:02:06.900 The dominant web application framework in Elixir is Phoenix, which has a standard folder layout that it adheres to. If you're looking to get started with Elixir, I highly recommend the 'Little Elixir and OTP Guidebook.' It took me several recommendations before I finally bought it, and it has been a great resource. My advice to you, if you're new to Elixir, is to embrace the different ways of thinking that come with functional programming. It might feel odd at first, but trying to engage with the community practices will help you adjust.
00:02:40.260 There are some videos linked that I would like to share. The first one, titled 'Giving Up the Object-Oriented Ghost,' addresses the initial reactions you may have when starting in a functional environment. There are more resources available if you want to dive deeper. Since I work a lot with Active Record, I found the 'Leveling Up with Ecto' session engaging, and the one about abstractions offered some deep insights.
00:03:15.959 The title of this talk is 'Object-Oriented Thinking.' To provide some background, on the left side of the slide, you'll see the definition of object-oriented programming according to Alan Kay. On the right side, you can see the definition of the Erlang worldview according to Joe Armstrong. While I removed some details to keep the focus, you will see that these two definitions share significant similarities for our discussion.
00:04:02.300 In the Ruby world, everything is an object. Conversely, in Erlang, everything is a process. In Ruby, objects communicate through sending and receiving messages. In Erlang, this interaction occurs via message passing as well, making it the only way for processes to communicate. Objects maintain their own memory, and in Erlang, processes are strongly isolated from one another.
00:04:38.990 This encapsulated messaging and state management is what I mean by object-oriented thinking. In Ruby, message passing involves calling a method, while in Erlang, the Actor Model introduces the concept that every process is uniquely identified by a name (or PID) and has its own mailbox. You communicate with any process by sending messages to its mailbox, and responses are received in your own mailbox.
00:05:19.740 Erlang added support for symmetric multiprocessing in 2006, meaning if you have multiple cores available, it will use them all for enhanced performance. This is similar to what Apple did with Grand Central Dispatch. Another significant advantage in the Erlang ecosystem is the concept of distributed Erlang, allowing you to run your processes across multiple machines. This ability to scale distributed systems is important for many use cases.
00:05:35.130 Now, I'm going to present a contrived Ruby example of a simple chat application. In this application, users can post messages, retrieve their message history, and reset their chat history. Additionally, a private method records each message with a timestamp. This basic structure should be familiar to most developers.
00:06:34.040 Next, I'll show you an Elixir version of the same idea, where the emphasis is placed on the messages being passed around. In Elixir, the corresponding methods are still similar in functionality: posting messages, retrieving history, and resetting history. The internal state is managed by passing messages, allowing us to define the state throughout the application code.
00:07:09.850 In functional programming, one of the challenges for object-oriented programmers is preserving state. The solution in Elixir is leveraging its recursion and the 'received' block, which resembles the method missing functionality we discussed earlier. In this received block, we handle processing and responses while utilizing recursive calls to maintain state without running into issues like stack overflow that would occur in Ruby.
00:07:42.400 To interact with this state, we spawn a separate process. I'm providing some example code so you can see how it looks in the context of Elixir. I'm using the interactive Elixir shell (IEx) here to demonstrate how we can spawn the process, receive its PID, and send messages to it. The asynchronous nature of this messaging pattern means that when we send a message, it goes into the mailbox of the process.
00:08:07.680 As a reminder of our earlier contrived Elixir example, when we call 'get history,' we send a message that uses the PID of the requesting process to retrieve the history smoothly. However, to see the output, we must call a function to flush the results from our mailbox.
00:09:04.700 Having processes capable of sending and receiving messages is great, but maintaining asynchronous interactions can be challenging. We need a better structure to manage state, so I'll introduce the OTP framework, which provides templates to guide us.
00:09:38.910 For instance, when starting a process using the GenServer behavior, we can link it to the parent process while taking advantage of the built-in error handling and supervision tree. Using GenServer also communicates our intentions to other developers and enhances maintainability. Our client API remains similar, allowing us to post messages, get history, and reset the chat.
00:10:24.700 The calls in the GenServer framework will often vary between synchronous (using call) and asynchronous (using cast) operations. This distinction allows more effective message handling, and we can organize our code into smaller, manageable functions rather than one large receive block. Each of these functions handles state through variables passed around in the process.
00:11:00.360 In conclusion, GenServer offers a way to maintain state, manage processes, and keep track of our application in a resilient manner. The 'init' and 'terminate' functionalities provide a rigorous setup and teardown process, resembling the setup and teardown found in object-oriented languages. With this introduction to the operational principles of OTP and Elixir, the hope is to provide a starting point for further exploration.
00:11:46.579 If you have any questions, I'd be happy to discuss them with you around the conference.