I’m preparing a talk for some local folks about what it’s like to develop in Elixir/Erlang. They want me to hit up on some topics. These are my notes for the talk.
Local web developers, some Ruby/Rails, NodeJS, Clojure, PHP.
The big question:
What’s the sell? Why would I use this over solution X?
As developers, every day we deal with problems of scale, reliability. and we’ve been mostly tackling these problems with the same kinds of languages and paradigms.
At this talk, learn what makes Elixir/Erlang such a unique development environment. You’ll leave with:
- Some history and background knowledge of Erlang and Elixir
- A new way to think about constructing a system and programming in general
- A better knowledge of Elixir/Erlang syntax
- A starting place to write a web-app in Elixir
- a simple, distributed program
- Basic web app
Erlang is old (ahem, “mature”)
- C# (2000)
- Visual Basic (1991)
- Python (1991)
- Bash (1989)
- Erlang (1987)
… Objective-C! (1983)
It is mature. For 40 years its libraries have grown and aged, all by a relatively small group of people. That obscurity has allowed a focused set of developers many years to distill some oak-barrel aged software. It’s still actively maintained, with the most recent release at the time of writing, being Erlang/OTP 19.2 on Dec 2016!
Erlang was designed to meet very stringent requirements
Erlang had an almost inhuman set of requirements that it had to meet. In “Making reliable
distributed systems in the presence of software errors” (Armstrong 2003), Joe Armstrong lays them all out. Look at this list:
- The system must be able to handle very large numbers of concurrent activities.
- Actions must be performed at a certain point in time or within a certain time.
- Systems may be distributed over several computers.
- The system is used to control hardware.
- The software systems are very large.
- The system exhibits complex functionality such as, feature interaction.
- The systems should be in continuous operation for many years.
- Software maintenance (reconfiguration, etc) should be performed without stopping the system.
- There are stringent quality, and reliability requirements.
- Fault tolerance both to hardware failures, and software errors, must be provided.
Though Erlang was used at Ericsson to make some telephony switches, make no mistake, Erlang is a general purpose language. It’s versatile enough to solve a wide variety of problems, but has a design that seems perfect for today’s infrastructure requirements.
José Valim is a core contributor to Rails, but in the process of adding more concurrency to Rails, he ran into some problems he couldn’t figure out. He decided to look to other systems for inspiration (Interview with José Valim @ 23:30). After reading the Erlang chapter in “Seven Languages in Seven Weeks” (Tate, 2010), he found what he was looking for. While Erlang was amazing, it was built and used by high-functioning software developers who didn’t find it unusual to suggest you solve problems by saying “you should just write a parser for that”.
In Erlang, he saw opportunities to write better software than he thought possible, but didn’t think others would want to jump through the hurdles he saw. With that in mind, he created a relatively thin language that would be compiled into Erlang, but would feel more at home for developers used to Ruby syntax. He also thought if the language were to succeed it would need modern tooling so he made sure a lot of effort went into creating a REPL (iex), build tool (Mix), package manager (Hex), and unit-testing framework (ExUnit). While he was at it, he researched a lot of different languages and worked hard to incorporate what he thought were the best ideas from many of them.
The language itself trivially resembles Ruby, but the programming paradigms of Erlang and Ruby are so different that at first I was confused because I was expecting the Ruby paradigm to have transferred as well. Make no mistake, when you speak, you might use words from Ruby, but you are speaking Erlang paragraphs.
All Elixir code compiles into Erlang code. It can be called from Erlang and Erlang can be called from Elixir. Elixir adds a few useful operators and a genuine macro system that lets you code hooks into the compiler that take an AST and spit out a new one before compiling into bytecode.
It is not trying to replace Erlang. In fact the authors state explicitly that if an established library exists in Erlang, use it from Elixir, do not try to make a “Elixiry” version of it. You are encouraged to learn both languages even if you code mainly in one.
I’m reminded of Objective-C in a few ways. Objective-C added a very dynamic form of object-oriented programming to basic C, but both paradigms are contained in the same file. There is no “bridge” code that has to be imported in. Elixir is the same in that you can call libraries written in pure Erlang from your Elixir source files.
This is a full featured modern web-framework (probably the closest comparison would be Rails). Probably not going to talk about this too much right now (though I’d be happy to come back and talk about it more in-depth later).
You can imagine that once Erlang started getting more traction, folks were wanting to write “Erlang-Rails”. A few web-server projects have come and gone, but the one that currently has the most momentum is Phoenix. If you have programmed in Rails, there is a lot of furniture here you will recognize.
So, how to start really talking about Erlang?
I could show you the syntax. Does it use braces? (no). Does it have closures like ruby (yes)? How do I write a class in Erlang (gtfo)? Does it have language feature X? Show me the code, I want to be dazzled by syntax and language features!!
Listen, programming languages are written to make certain programming paradigms easier, so before I show you any Elixir code, I want to talk about how you build programs in Erlang. If I showed you syntax or source, you’d probably have your heads all in the clouds trying to interpret the source files and I want you thinking about what all this enables, because that is part of what is really special.
I’ll show you the code in a bit, but for now, pay attention to this part.
Building with Processes
Mac OS (before X)
MacOS before Mac OS X, we can call it “Mac OS Classic” , had shared memory across processes. If you wrote a program that dereferenced null, the WHOLE SYSTEM would wreck. The. Whole. System. When your program was loaded, you were loaded into the system process and joined an event loop exposed by the system that was shared by EVERY PROGRAM (this was called “cooperative multitasking” and was actually pretty great if you were writing a game).
Because of shared memory, you could write into any memory location you liked, including the memory of any other apps. Because of cooperative multitasking, you could, like an asshole, never return from that event loop hook and take over the entire cpu.
My old-Mac friends and me sometimes say this…
“Remember when your app would take down the entire OS? Wasn’t that f‑ed?”
Something more civilized
Now, we have modern OSes with memory protection between processes (via virtual memory) and preemptive multitasking (you can’t steal the CPU away in your app). Your app can’t kill the OS and your app isn’t in control of CPU usage any more.
This is a huge huge relief, but why stop there?.
In the same way that it was not okay that on the Mac you could write an app that would take out the OS, why is it somehow okay that you can write a library that can be linked into other processes, but a bug in your library can take out the entire process it’s linked to?
I’m not sure how to get around that but…
What if instead of making an app from piecing together different libraries, you made an app from piecing together different processes, each isolated from each other, both in memory, and in execution? And what if the language made it so easy to get processes to talk to each other, that sending messages between processes was as easy as calling a library or calling a method on an object?
Erlang enables you to make systems by putting together not libraries, not objects, but processes. The design-granularity of processes in an Erlang program are roughly the size of classes in other languages. Your whole app is made from tiny, tiny processes, running in isolation from of the others. Taking out one of those processes takes out a vanishingly small part of the system.
Think of someone holding up a single pane of glass in a frame. If you shoot it with a bullet, the whole thing is going to break. Now think of someone holding up a frame composed of 1000 small panes with frames around them. If you shoot that with a bullet, only a very small part of it breaks.
…also in Erlang, that newly broken glass is going reappear like magic, but we’ll get to that.
This is part of what makes systems written in Erlang so resilient. This is the programming paradigm that the language enables.
“Remember when a bug in your library would take out the whole app? Wasn’t that f‑ed?”
Are you sure you mean “Process”?
At this point, I need to be super-duper clear about the word “process”. When we say “process” in the context of Erlang, we do not mean an OS process. Here’s what qualifies the word “process” in this context:
- Concurrent execution (parallel over multi-cores)
- Memory Isolation
On Unix, OS processes are concurrent (parallel with multiple cores) and memory-isolated, but they are fairly heavyweight with each process getting its own virtual memory system. Threads are lighter-weight, still run across cores, but share memory with other threads. “Green” threads are lighter still as they are provided not through calls to the OS, but are “emulated” in the virtual machine itself as it executes bytecode. They still share memory with other threads.
Why are processes different in Erlang?
Here’s the best explanation I could find (from Stack Overflow which is quoting a presentation by Joe Armstrong)
[Erlang] is a concurrent language – by that I mean that threads are part of the programming language, they do not belong to the operating system. That’s really what’s wrong with programming languages like Java and C++. Its threads aren’t in the programming language, threads are something in the operating system – and they inherit all the problems that they have in the operating system. One of the problems is granularity of the memory management system. The memory management in the operating system protects whole pages of memory, so the smallest size that a thread can be is the smallest size of a page. That’s actually too big.
If you add more memory to your machine – you have the same number of bits that protects the memory so the granularity of the page tables goes up – you end up using say 64kB for a process you know [is] running in a few hundred bytes.
My current understanding is:
- They are more like green threads in that they are provided by the VM, not the OS.
- They do not share memory because the language only allows you to access immutable data structures.
- The VM can schedule execution across cores.
(but I have not read the code)
Locks. Never again!
One other thing worth considering. Erlang doesn’t have threads. You no longer have to write code with locks and semaphores and mutexes and monitors and signals and just shoot me already.
Ironically, in this concurrent language you are writing single-threaded programs.
Sending messages between processes is the core of your system
How do processes talk to each other in Erlang? The language has primitives for sending a message (any data structure really) to another running process. The Erlang runtime provides an “mailbox” for processes where the sent message is stored until the process receives the message by choosing to look through its mailbox. If part of the message has a reference to the sending process, then the recipient can choose whether to send a message back. The language doesn’t guarantee to the sender that the message was received.
You can think of a process as an “object” in the (other) OO languages you’ve used and you can think of sending a message to a process like calling a method on an instance that has no return value.
In fact, it’s so similar that…
Erlang is Object-Oriented
Alan Kay is famous for coming up with the idea of Object-Oriented Programming (as espoused in the Smalltalk language which he developed).
What he came up with is much different than what you probably think of when you think of the word “Object-Oriented”.
OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I’m not aware of them.
With that in mind, check out this interview with Joe Armstrong about Erlang:
Alan Kay himself wrote this famous thing and said “The notion of object oriented programming is completely misunderstood. It’s not about objects and classes, it’s all about messages”. He wrote that and he said that the initial reaction to object oriented programming was to overemphasize the classes and methods and under emphasize the messages and if we talk much more about messages then it would be a lot nicer. The original Smalltalk was always talking about objects and you sent messages to them and they responded by sending messages back.
The 3 things that object oriented programming has is “messaging”, which is possibly the most important thing. The next thing is “isolation” and that’s what I talked about earlier, that my program shouldn’t crash your program, if the 2 things are isolated, then any mistakes I make in my program will not crash your program. This is certainly not true with Java. You cannot take 2 Java applications, bung them in the JVM and one of them still halts the machine and the other one will halt as well. You can crash somebody else’s application, so they are not isolated.
The third thing you want is “polymorphism”. Polymorphism is especially regarding messaging, that’s just there for the programmer’s convenience. It’s very nice to have for all objects or all processes or whatever you call them, to have a printMe method - “Go print yourself” and then they print themselves. That’s because the programmers, if they all got different names, the programmer is never going to remember this, so it’s a polymorphism. It just means “OK, all objects have a printMe method. All objects have a what’s your size method or introspection method.”
Erlang has got all these things. It’s got isolation, it’s got polymorphism and it’s got pure messaging. From that point of view, we might say it’s the only object oriented language and perhaps I was a bit premature in saying that object oriented languages are about. You can try it and see it for yourself.
My own note: the “polymorphism” Joe is referring to above is what you’d call “duck-typing” in other other languages. You send the same message to different objects and they behave differently.
This is getting long
I was originally going to start writing code here to show you exactly what this looks like in Elixir, but I’ll save it for Part 2.
In Part 2, we’ll make a process and send messages to it and receive messages from it.