I’ve spent a lot of the last month or so learning Erlang in my free time. It’s been a very rewarding experience.
I wanted to look at Erlang because for a number of years now I’ve been coming across very impressive systems (amongst others RabbitMQ, CouchDB & Riak) written using this language and its OTP framework. A number of the core ideas in Erlang also aligned very well with a number of my recent interests in IT, including distributed systems design, building reliable systems, functional approaches to programming, messaging, and building systems that can support working with ‘big data’. Additionally I’ve noticed over the past few years the growing influence of the ideas that Erlang has at its core, in particular the Actor approach to concurrency (which now has a firm place in the Scala/Java world thanks to the Akka framework).
So enough background to why I have found myself ever more enamoured with Erlang, what have I learned which may help you to get coding in Erlang?
Well, for starters code is organised into modules, the modules have a name (which you give them), and the file which they’re saved in will share this name, but have the extension ‘.erl’.
So your first move might be to create a file ‘hello_erlang.erl’. In that add the code:
-module(hello_erlang).
Note two things about this code straight away:
- The text ‘hello_erlang’ is not in quotes, this is because it’s not a string. Erlang has the concept of an atom. More on these later, but be aware that this is an atom;
- The full stop at the end of the line. Erlang writes a lot like English does, so as you finish a sentence with a full-stop in English, so you finish a statement in Erlang with a full-stop.
Straight away you have something which, once saved, can be compiled. So let’s do that. Save it, and then start the erlang console.
Couple of things at this point.
- I’m going to assume that you have erlang installed. If you don’t then I recommend the instructions I found on the Basho site, they’ve got me going on both Ubuntu & MacOS very nicely. You can find them here: http://wiki.basho.com/Installing-Erlang.html (brew FTW on the MacOS). If using Windows I’d recommend running up a VM to use and installing something like Ubuntu or CentOS on it, but JIC you don’t want to do that you can find it here http://www.erlang.org/download.html.
- I’m assuming that, like me, you’re doing your editing in emacs & happy at a command prompt. If neither of these things are true then I’d strongly recommend that you make them true. Getting the basics of emacs only takes about an hour, but Erlang has some integration with emacs out of the box, has other projects which offer further integration, and it’ll be worth the initial WTFness of it I think.
Ok, so you’ve got erlang installed now (and hopefully emacs too), now navigate (in the command prompt/bash) to the folder where you saved hello_erlang.erl and start erlang (type erl & hit enter). What this will give you is the erlang repl console (Read Evaluate Print Loop), in many ways like the ones you may have come across if using like Ruby or F#. From here you can straight away try some things & see what happens, like:
- Fred = “Fred”.
- note the full stop to complete the statement
- Fred == “Fred”.
- should evaluate to true
- Fred = “Bert”.
- should complain that ‘no match of right hand side value “Bert”‘. This is because once a variable has been assigned a value, it cannot be assigned another value. Like you’ll find in F#, variables are immutable.
- fred == Fred.
- will give false, you just compared an atom to a variable that holds the value of a string
- Bob = bob.
- assigns the atom bob to the variable Bob
- Bob == bob.
- will evaluate to true as the variable Bob holds the atom bob, which is clearly the same as the atom bob
- true == false.
- will evaluate to false because the atom true is not equal to the atom false.
More of the basic types in Erlang in a future post, but worth noting here that:
- Variables always start with a capital letter;
- An atom always starts with a lower case letter;
- Variables can point to both atoms & strings (& other things, but we haven’t seen them yet).
I tend to think of atoms as kind of like powerful enums, but this comparison isn’t really fair. They’re so much more than that. Every function name, module name, true, false, etc… they’re all atoms. You don’t have to declare them up front, at the point where they are used, they exist, and they continue to exist from then forwards. (there are some gotchas around this, it’s not quite that simple, but we’ll worry about that in the future)
Anyhow, back to that module. Still in the erlang console write
c(hello_erlang).
This will compile your module and you should get this response:
{ok,hello_erlang}
Which gives us a good moment to introduce another important type in Erlang, the tuple. .NET had a tuple type introduced (IIRC) in version 4 of the framework which allows you to write things like
var myTuple = new Tuple<string,int>();
or even,
var myTuple = Tuple.New("Fred",42).
Well, erlang lets you simply write:
{"Fred",42}
for exactly the same effect. Tuples are an incredibly important thing in erlang & we’ll see a lot more of them, but for now I think it’s enough just to be aware that the response you just got by compiling your module was a tuple consisting of two atoms, and it tells you that the module was compiled ‘ok’. Which is nice.
So anyway, lets add some code to that module, and like all good devs (cough, cough) let’s start with a test.
Erlang ships with a JUnit like (so, NUnit like for those of us of a .NET persuasion) testing framework called EUnit (strange that isn’t it). To use EUnit we’re going to add some code that’s the equivalent of a using/import statement and then we’ll be able to write our test.
With the hello_erlang.erl file back open again in the editor of your choice (which I trust is emacs – though did I mention that there is Eclipse integration available for Erlang too? No? Well, move along, nothing to see here,…) add this line of code below the -module(hello_erlang). line.
-include_lib("eunit/include/eunit.hrl").
With that in place we can now write our first erlang function, here it is:
greeting_should_be_hello_erlang_test() ->?assertEqual("Hello Erlang!", greeting()).
Few things to note here:
- That the function name is an atom, and that the empty brackets signify that it expects no arguments to be supplied when it’s called – basically it looks a lot like a C# or Java method signature, but with out the superflous crap like access modifiers, or having to declare a return type.
- That appending _test to the test name is mandatory. It’s how the test library will be able to recognise this function as one to execute as a test. There are no attributes/annotations available in Erlang, so functions can’t have metadata attached to them, this means that conventions such as this are necessary. Java people, you may remember this sort of thing from working with JUnit in pre 1.5 versions of Java. If I’m honest this is a bit disappointing to me, but really and truly I don’t thing it matters much.
- The -> bit is basically identical to => in C#. It denotes that a function definition is about to be provided. Java people, apologies, but if you’re going to use a dead language then this may look unfamiliar, get on the scala train & then come back :p
- the ? tells us that assertEqual is a macro. More on them later, a lot later.
- We expect that the function greeting() will be equal the string “Hello Erlang”. Note, I didn’t say that the result of the function should equal the string “Hello Erlang”, especially as the greeting function takes no arguments, we should expect referential transparency!
- No curly brace crap, but meaningful indentation.
- We’ve embedded the test in the same file as the code we’re writing. We don’t have to do this, but I find it works quite well when doing TDD. Compiler directives can be used to ensure that the tests don’t make their way into the released versions of the compiled code.
greeting() ->"Hello Erlang!".
hello_erlang:test().
hello_erlang:greeting().
** exception error: undefined function hello_erlang:greeting/0
-export([greeting/0]).
- The square brackets signify a list [] (definitely more to be said about lists, but not in this post).
- The /0 following on from greeting signify that this is a function with 0 arguments expected. You saw it earlier in the error message that erlang gave when we tried to execute the function before we had exported it.
Great stuff and looking forward to reading more, I found this useful too in getting going quickly:
http://www.erlang.org/documentation/doc-5.1/doc/getting_started/getting_started.html