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”.
- 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.
So, now we need to create our greeting function (yes I know I’m skipping the red in red, green, refactor). This is really trivial:
greeting() ->
"Hello Erlang!".
With that added to your file (personally I’d put it between the module declaration & the include_lib statement), go back to the erlang console, recompile your code and the run your tests by writing:
hello_erlang:test().
This should result in a message that tell’s you your tests passed (all one of them). That colon is very similar to how we use a full stop to separate the name of a class from the name of one of its methods when coding in C# or Java. So where we would have written hello_erlang.test in one of those languages, here we use a colon. Simple.
Try this though to execute the greeting function:
hello_erlang:greeting().
You should get this message:
** exception error: undefined function hello_erlang:greeting/0
It fails because we haven’t exported the function, or in terms of Java/C# made it public. By default all functions are not exported (available outside the module that they’re declared in). The test method becomes available because of our use of the ?assertEqual macro, and so we are not required to export that.
To export the function add this line of code after the module declaration statement:
-export([greeting/0]).
Couple of things to note here:
- 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.
And for now, that’s it. I hope from this, if you’ve never seen erlang before you’ve been able to get going. But you haven’t really seen anything of either the beauty & power of the language, the OTP framework, or the other libraries & tools available for it (such as the incomparable WebMachine). In future posts I’ll try & cover these too.
Like this:
Like Loading...