Agent Smith is as a program that kills other programs, or simulated people, that disrupt the simulated reality known as the Matrix. The most basic trait that makes him dangerous is his ability to appear human. In this section, we’re going to look at Erlang’s ability to build general-purpose applications. I’m going to try my best to give you “normal.” It’s not going to be easy.
If you started this book as a pure object-oriented programmer, you may struggle a little bit, but don’t fight it. You’ve already seen code blocks in Ruby, actors in Io, pattern matching in Prolog, and distributed message passing in Scala. These are foundational principles in Erlang. This chapter will start with another important concept. Erlang is the first of our functional languages. (Scala is a hybrid functional/object-oriented language.) To you, that means the following:
Your programs are going to be built entirely out of functions, with no objects anywhere.
Those functions will usually return the same values, given the same inputs.
Those functions will not usually have side effects, meaning they will not modify program state.
You will only be able to assign any variable once.
Living by the first rule is mildly challenging. Living by the next three can knock you down, at least for a little while. Know that you can learn to code this way, and the result will be programs that are built from the inside out for concurrency. When you remove mutable state from the equation, concurrency gets dramatically simpler.
If you paid close attention, you caught the word usually in the second and third rules. Erlang is not a pure functional language; it does allow a few exceptions. Haskell is the only pure functional language in this book. But you will get a strong flavor of functional-style programming, and you will code to these rules more often than not.
I’m working with Erlang version R13B02, but the basic stuff in this chapter should work OK on any reasonable version. You’ll get to the Erlang shell by typing erl (werl on some Windows systems) at the command line, like this:
batate$ erl
|
|
Erlang (BEAM) emulator version 5.4.13 [source]
|
|
|
|
Eshell V5.4.13 (abort with ^G)
|
|
1>
|
We’ll do most of our work there, early on, as with other chapters. Like Java, Erlang is a compiled language. You’ll compile a file with c(filename). (you need the period at the end). You can break out of the console, or a loop, with Control+C. Let’s get started.
Let’s get some of the basic syntax out of the way. Crack open the console, and type the following:
1> % This is a comment
|
That was simple enough. Comments start with a % and eat everything until the end of a line. Erlang parses a comment as a single space.
1> 2 + 2.
|
|
4
|
|
2> 2 + 2.0.
|
|
4.0
|
|
3> "string".
|
|
"string"
|
Each statement ends in a period. These are some of the basic types: strings, integers, and floats. Now, for a list:
4> [1, 2, 3].
|
|
[1,2,3]
|
As with the Prolog family of languages, lists are in square brackets. Here’s a little surprise:
4> [72, 97, 32, 72, 97, 32, 72, 97].
|
|
"Ha Ha Ha"
|
So, a String is really a List, and Agent Smith just laughed at your mamma. Oh, those social skills. 2 + 2.0 tells us that Erlang does some basic type coercion. Let’s try to break a line of code with a bad type:
5> 4 + "string".
|
|
** exception error: bad argument in an arithmetic expression
|
|
in operator +/2
|
|
called as 4 + "string"
|
Unlike Scala, there’s no coercion between strings and ints. Let’s assign a variable:
6> variable = 4.
|
|
** exception error: no match of right hand side value 4
|
Ah. Here, you see the ugly side of the comparison between agents and Erlang. Sometimes, this pesky language has more brain than soul. This error message is really a reference to Erlang’s pattern matching. It’s breaking because variable is an atom. Variables must start with an uppercase letter.
7> Var = 1.
|
|
1
|
|
8> Var = 2.
|
|
|
|
=ERROR REPORT==== 8-Jan-2010::11:47:46 ===
|
|
Error in process <0.39.0> with exit value: {{badmatch,2},[{erl_eval,expr,3}]}
|
|
|
|
** exited: {{badmatch,2},[{erl_eval,expr,3}]} **
|
|
8> Var.
|
|
1
|
As you can see, variables begin with a capital letter, and they are immutable. You can assign each value only once. This concept gives most first-time programmers trouble within a functional language. Let’s introduce some data types with more complexity.
In functional languages, symbols become more important. They are the most primitive data element and can represent anything you want to name. You’ve encountered symbols in each of the other programming languages in this book. In Erlang, a symbol is called an atom and begins with a lowercase character. They are atomic values that you can use to represent something. You’ll use them like this:
9> red.
|
|
red
|
|
10> Pill = blue.
|
|
blue
|
|
11> Pill.
|
|
blue
|
red and blue are atoms—arbitrary names that we can use to symbolize real-world things. We first return a simple atom called red. Next, we assign the atom called blue to the variable called Pill. Atoms get more interesting as you attach them to more robust data structures that we’ll see a little later. For now, let’s build on the primitives by looking at the list. You’ll represent lists with square brackets:
13> [1, 2, 3].
|
|
[1,2,3]
|
|
14> [1, 2, "three"].
|
|
[1,2,"three"]
|
|
15> List = [1, 2, 3].
|
|
[1,2,3]
|
So, the list syntax is familiar. Lists are heterogeneous and can be any length. You can assign them to variables, just as you would a primitive. Tuples are fixed-length heterogeneous lists:
18> {one, two, three}.
|
|
{one,two,three}
|
|
19> Origin = {0, 0}.
|
|
{0,0}
|
There are no surprises here. You can see the strong Prolog influence here. Later, when we cover pattern matching, you will notice that when you match a tuple, the size will matter. You can’t match a three-tuple to a two-tuple. When you match a list, the length can vary, just as it did in Prolog.
In Ruby, you use hash maps to associate names with values. In Erlang, you’ll often see tuples used as you would use maps or hashes:
20> {name, "Spaceman Spiff"}.
|
|
{name,"Spaceman Spiff"}
|
|
21> {comic_strip, {name, "Calvin and Hobbes"}, {character, "Spaceman Spiff"}}.
|
|
{comic_strip,{name,"Calvin and Hobbes"},
|
|
{character,"Spaceman Spiff"}}
|
We’ve represented a hash for a comic strip. We use atoms for the hash keys and use strings for the values. You can mix lists and tuples as well, such as a list of comics, represented by tuples. So, how do you access the individual elements? If Prolog is fresh in your mind, you’re already thinking in the right direction. You’ll use pattern matching.
If you worked through the Prolog chapter, you got a pretty solid foundation of pattern matching. I want to point out one major difference. In Prolog, when you defined a rule, you matched all the values in the database, and Prolog worked through all the combinations. Erlang works like Scala. A match will work against a single value. Let’s use pattern matching to extract the values from a tuple. Say we have a person:
24> Person = {person, {name, "Agent Smith"}, {profession, "Killing programs"}}.
|
|
{person,{name,"Agent Smith"},
|
|
{profession,"Killing programs"}}
|
Let’s say we want to assign the name to Name, and the profession to Profession. This match would do the trick:
25> {person, {name, Name}, {profession, Profession}} = Person.
|
|
{person,{name,"Agent Smith"},
|
|
{profession,"Killing programs"}}
|
|
26> Name.
|
|
"Agent Smith"
|
|
27> Profession.
|
|
"Killing programs"
|
Erlang will match up the data structures, assigning variables to the values in the tuples. An atom will match itself, so the only work to be done is to match the variable Name to "Agent Smith" and the variable Profession to "Killing programs". This feature works much like it does in Prolog and will be the fundamental decision-making construct that you use.
If you are used to Ruby or Java-style hashes, it may seem strange to have the initial atom of person. In Erlang, you’ll often have multiple matching statements and multiple kinds of tuples. By designing your data structures this way, you can quickly match all person tuples, leaving the others behind.
List pattern matching is similar to Prolog’s:
28> [Head | Tail] = [1, 2, 3].
|
|
[1,2,3]
|
|
29> Head.
|
|
1
|
|
30> Tail.
|
|
[2,3]
|
Easy as one, two, three. You can bind to more than one variable at the head of a list, too:
32> [One, Two|Rest] = [1, 2, 3].
|
|
[1,2,3]
|
|
33> One.
|
|
1
|
|
34> Two.
|
|
2
|
|
35> Rest.
|
|
[3]
|
If there are not enough elements in the list, the pattern won’t match:
36> [X|Rest] = [].
|
|
** exception error: no match of right hand side value []
|
Now, some of the other error messages make a little more sense. Let’s say you forget to start your variables with an uppercase letter. You’ll get this error message:
31> one = 1.
|
|
** exception error: no match of right hand side value 1
|
As you’ve seen before, the = statement is not a simple assignment. It is actually a pattern match. You’re asking Erlang to match the integer 1 with the atom one, and it can’t.
Sometimes, you need to access data at the bit level. If you’re cramming more data into less space or dealing with predefined formats such as JPEGs or MPEGs, the location of each bit matters. Erlang lets you pack several pieces of data into one byte quite easily. To do these two things, you need two operations: pack and unpack. In Erlang, a bitmap works just like other types of collections. To pack a data structure, you’ll just tell Erlang how many bits to take for each item, like this:
1> W = 1.
|
|
1
|
|
2> X = 2.
|
|
2
|
|
3>
|
|
3> Y = 3.
|
|
3
|
|
4> Z = 4.
|
|
4
|
|
5> All = <<W:3, X:3, Y:5, Z:5>>.
|
|
<<"(d">>
|
The << and >> bracket binary patterns in this constructor. In this case, it means take 3 bits for the variable W, 3 bits for X, 5 bits for Y, and 5 bits for Z. Next, we need to be able to unpack. You can probably guess the syntax:
6> <<A:3, B:3, C:5, D:5>> = All.
|
|
<<"(d">>
|
|
7> A
|
|
7> .
|
|
1
|
|
8> D.
|
|
4
|
Just like tuples and lists, we just supply the same syntax and let pattern matching do the rest. With these bitwise operations, Erlang is surprisingly powerful for low-level tasks.
We’re covering a lot of ground pretty quickly, because you’ve already been introduced to all the major concepts in the chapter. Believe it or not, we’re almost through the first day of the Erlang chapter, but we first need to introduce the most important concept, the function.
Unlike Scala, Erlang is dynamically typed. You won’t have to worry too much about assigning types to data elements. Like Ruby, Erlang typing is dynamic. Erlang will bind types at run time, based on syntactic clues such as quotes or decimal points. At this point, I’m going to crack open a fresh copy of the console. Let me introduce a few terms. You’re going to write functions in a file with an .erl extension. The file contains code for a module, and you have to compile it to run it. After you’ve compiled a file, it produces a .beamexecutable. The .beam compiled module will run in a virtual machine called the beam.
With the housekeeping out of the way, it’s time to create some basic functions.
I’m going to enter a file that looks like this:
| erlang/basic.erl | |
-module(basic).
|
|
-export([mirror/1]).
|
|
|
|
mirror(Anything) -> Anything.
|
|
The first line defines the name of the module. The second line defines a function that you want to use outside of the module. The function is called mirror, and the /1 means it has one parameter. Finally, you get to the function itself. You can see the influence of the Prolog-style rule. The function definition names the function and determines the arguments. Afterward, you have the -> symbol, which simply returns the first argument.
With a function definition complete, I’ll fire up the console from the same directory that has the code file. I can then compile it like this:
4> c(basic).
|
|
{ok,basic}
|
We compiled basic.erl, and you will find a basic.beam file in the same directory. You can run it like this:
5> mirror(smiling_mug).
|
|
** exception error: undefined shell command mirror/1
|
|
6> basic:mirror(smiling_mug).
|
|
smiling_mug
|
|
6> basic:mirror(1).
|
|
1
|
Notice that it is not enough to have the function name alone. You also need to include the module name, followed by a colon. This function is dead simple.
Notice one thing. We were able to bind Anything to two different types. Erlang is dynamically typed, and to me, it feels good. After Scala’s strong typing, I’m coming home from a weekend in Siberia, or at least Peoria.
Let’s look at a function that’s slightly more complicated. This one defines several matching alternatives.
You can create a matching_function.erl file like this:
| erlang/matching_function.erl | |
-module(matching_function).
|
|
-export([number/1]).
|
|
|
|
number(one) -> 1;
|
|
number(two) -> 2;
|
|
number(three) -> 3.
|
|
And you can execute it like this:
8> c(matching_function).
|
|
{ok,matching_function}
|
|
9> matching_function:number(one).
|
|
1
|
|
10> matching_function:number(two).
|
|
2
|
|
11> matching_function:number(three).
|
|
3
|
|
12> matching_function:number(four).
|
|
** exception error: no function clause matching matching_function:number(four)
|
This is the first function I’ve introduced with multiple matching possibilities. Each possible match has the function name, the argument to match, and the code to execute after the -> symbol. In each case, Erlang just returns an integer. Terminate the last statement with . and all others with ;.
Just as with Io, Scala, and Prolog, recursion will play a big role. Like Prolog, Erlang is optimized for tail recursion. Here is the obligatory factorial:
| erlang/yet_again.erl | |
-module(yet_again).
|
|
-export([another_factorial/1]).
|
|
-export([another_fib/1]).
|
|
|
|
another_factorial(0) -> 1;
|
|
another_factorial(N) -> N * another_factorial(N-1).
|
|
|
|
another_fib(0) -> 1;
|
|
another_fib(1) -> 1;
|
|
another_fib(N) -> another_fib(N-1) + another_fib(N-2).
|
|
So, it’s another factorial, and it’s defined recursively just like all the others. While I was at it, I may as well include a Fibonacci series, too.
Let me try to make it worth your while this time:
18> c(yet_again).
|
|
{ok,yet_again}
|
|
19> yet_again:another_factorial(3).
|
|
6
|
|
20> yet_again:another_factorial(20).
|
|
2432902008176640000
|
|
21> yet_again:another_factorial(200).
|
|
788657867364790503552363213932185062295135977687173263294742533244359
|
|
449963403342920304284011984623904177212138919638830257642790242637105
|
|
061926624952829931113462857270763317237396988943922445621451664240254
|
|
033291864131227428294853277524242407573903240321257405579568660226031
|
|
904170324062351700858796178922222789623703897374720000000000000000000
|
|
000000000000000000000000000000
|
|
22> yet_again:another_factorial(2000).
|
|
3316275092450633241175393380576324038281117208105780394571935437060380
|
|
7790560082240027323085973259225540235294122583410925808481741529379613
|
|
1386633526343688905634058556163940605117252571870647856393544045405243
|
|
9574670376741087229704346841583437524315808775336451274879954368592474
|
|
... and on and on...
|
|
0000000000000000000000000000000000000000000000000000000000000000000000
|
Ooooh-kaaay. That was certainly different. Now, you’re starting to see the butt-kicking side of the Agent Smith/Erlang comparison. If you didn’t take the time to run it, let me assure you, the results are absolutely instantaneous. I don’t know what the maximum integer size is, but I’m going to go out on a limb and say it’s big enough for me.
That’s a pretty good starting point. You’ve created some simple functions and seen them work. It’s a good time to wrap up day 1.
Erlang is a functional language. It is strongly, dynamically typed. There is not a lot of syntax, but what is there is not at all like the typical object- oriented languages.
Like Prolog, Erlang has no notion of an object. However, Erlang does have a strong connection to Prolog. The pattern matching constructs and multiple function entry points should look familiar to you, and you handle some problems in the same way, through recursion. The functional language has no notion of mutable state or even side effects. Maintaining program state is awkward, but you will learn a new bag of tricks. You’ll soon see the other side of the coin. Eliminating state and side effects will have a dramatic impact on how you will manage concurrency.
In the first day, you worked both in the console and with the compiler. Primarily, you focused on the basics. You created basic expressions. You also created simple functions. Like Prolog, Erlang lets a function have multiple entry points. You used basic pattern matching.
You also used basic tuples and lists. Tuples took the place of Ruby hashes and formed the foundation of data structures. You learned to pattern match across lists and tuples. These ideas will allow you to quickly attach behavior to tuples or interprocess messages in later chapters.
In day 2, I’m going to expand the basic functional concepts. We’ll learn how to build code that will work in a concurrent world, but we won’t actually go there yet. Take a little time to do some self-study to practice what you’ve learned so far.
The online community for Erlang is growing rapidly. A conference in San Francisco is picking up momentum. And unlike Io and C, you should be able to use Google to find what you need.
Find:
The Erlang language’s official site
Official documentation for Erlang’s function library
The documentation for Erlang’s OTP library
Do:
Write a function that uses recursion to return the number of words in a string.
Write a function that uses recursion to count to ten.
Write a function that uses matching to selectively print “success” or “error: message” given input of the form {error, Message} or success.