2. First: A little background
What is Erlang?
“Erlang is a functional concurrency-oriented language with extremely low-
weight user-space "processes", share-nothing message-passing semantics,
built-in distribution, and a "crash and recover" philosophy proven by two
decades of deployment on large soft-realtime production systems.”
- Facebook Engineering
3. First: A little background
Now in english…
● Functional before functional was “cool” (History)
● Designed from the ground-up to be highly concurrent with distribution
provided by a share-nothing message passing architecture.
● Messages can easily be passed to modules running in different VM nodes
or even on different servers.
● Processes are extremely lightweight (~512b vs. ~512MB for a java thread)
● Reliability takes top-priority. (Particularly when coupled with OTP) You are
encouraged to “let it crash” and restart rather than attempt to deal with a
failure. Given the stateless nature, this is typically not a problem.
● What does soft-real-time mean? In this case, as close to real-time as
possible. (see 10.5 here)
4. Getting it installed
Erlang Solutions provides all you need to get
started:
https://www.erlang-
solutions.com/downloads/download-erlang-otp
5. The Shell
$ erl
Erlang R16B03 (erts-5.10.4) [source] [smp:8:8] [async-threads:10] [kernel-poll:false]
Eshell V5.10.4 (abort with ^G)
1> 1 + 2.
3
2> 3 = 1 + 2.
3
3> 3 = 2 + 2.
** exception error: no match of right hand side value 4
4> Erlang = "Pure awesomeness!".
"Pure awesomeness!"
5> Erlang.
"Pure awesomeness!"
6> halt().
6. Language Basics
● Data Types
● Operators
● Lists
● Pattern Matching
● Functions
● Modules
● Recursion (a.k.a. What? No loops?)
● Processes (and PIDs)
● Example
7. Data Types
Numbers
● Integers -
o Base 10 by default
o Base can be specified using Base#value format
● Floats -
No magic here. 222.43 = 22.243e1.
Erlang R16B03 (erts-5.10.4) [source] [smp:8:8]
[async-threads:10] [kernel-poll:false]
Eshell V5.10.4 (abort with ^G)
1> 15 = 2#1111 = 10#15 = 8#17 = 16#0F.
15
8. Data Types
Atoms -
Atoms are a key concept in Erlang. you will use then A LOT! And you will learn to appreciate them.
Atoms are literals that must start will a lower-case letter. If you have a value that does not begin with a
lower-case letter or contains characters other than alphanumeric, underscore (_) or an at symbol (@),
you must enclose it in single quotes.
Eshell V5.10.4 (abort with ^G)
1> is_atom(atom).
true
2> is_atom(my_atom).
true
3> is_atom(node1@my_machine).
true
4> is_atom(‘node1@my_machine.erlang.org’).
true
9. Data Types
Atoms (continued) -
Caveat:
Atoms are really nice and a great way to send messages or represent constants. However there are
pitfalls to using atoms for too many things: an atom is referred to in an "atom table" which consumes
memory (4 bytes/atom in a 32-bit system, 8 bytes/atom in a 64-bit system). The atom table is not
garbage collected, and so atoms will accumulate until the system tips over, either from memory usage
or because 1048577 atoms were declared.
This means atoms should not be generated dynamically for whatever reason; if your system has to be
reliable and user input lets someone crash it at will by telling it to create atoms, you're in serious
trouble. Atoms should be seen as tools for the developer because honestly, it's what they are.
- Fred Hebert (Learn You Some Erlang)
10. Data Types
Strings -
Technically, strings are not an actual data type in Erlang. They are actually stored as a list of chars.
Adjacent strings are automagically concatenated at compile time.
Eshell V5.10.4 (abort with ^G)
1> "Hello" = [$H, $e, $l, $l, $o] = [72, 101, 108, 108, 111].
"Hello"
2> is_list("Hello").
true
Eshell V5.10.4 (abort with ^G)
1> "Hello World" = "Hello " "World".
"Hello World"
2> "Hello World" = "Hello " ++ "World".
"Hello World"
11. Data Types
Binaries and Bitstrings-
Erlang binaries are defined by <<value>>. It can be as simple as the following:
Erlang has many powerful abstractions for working with binaries. One basic example pattern matching:
For digging deeper (and I highly recommend you do) check out the Erlang bit syntax docs.
3> <<"Erlang Rocks!">>.
<<"Erlang Rocks!">>
Eshell V5.10.4 (abort with ^G)
1> <<E,R,L,A,N,G,S,R2,O,C,K,S2,E2>> = <<"Erlang
Rocks!">>.
<<"Erlang Rocks!">>
12. Data Types
Tuples -
Very straightforward representation of tuples. Tuples are used a lot in Erlang
and are particularly useful in pattern-matching.
Eshell V5.10.4 (abort with ^G)
1> MyTuple = {mytuple, 4}.
{mytuple,4}
2> is_tuple(MyTuple).
true
3> AlsoATuple = {four_element_tuple, {1,2,3,4}}.
{four_element_tuple,{1,2,3,4}}
4> is_tuple(AlsoATuple).
true
13. Data Types
Booleans -
In Erlang, there are no true booleans. Instead, booleans are represented by the
atoms ‘true’ and ‘false’. Other than that anomaly, there are a few unique
characteristics as you’ll see.
14. Comparison Operators
Erlang has all of the normal comparison operators you would expect to see
in any modern language. There are a few anomalies as you will see.
However, they are small and easy to adapt to.
1> 1 < 2.
true
2> 5 > 12.
false
3> 9 =< 9.
true
4> 24 == 24.0.
true
5> 24 =:= 24.0.
false
6> 5 /= 5.00000.
false
7> 5 =/= 5.00000.
true
Nothing unusual here.
Wait. What? (Hint: The second of these
compare type in addition to value.)
15. Comparison Operators (cont’d)
Erlang also has all the normal logical operators such as and, or, xor, and not.
The boolean operators and and or will always evaluate arguments on both
sides of the operator. If you want to have the short-circuit operators (which will
only evaluate the right-side argument if it needs to), use andalso and orelse.
1> true or false.
true
2> true and false.
false
3> true xor false.
true
4> not false.
true
5> not true.
false
6> ((true or false) or (true and false)).
true
16. Lists
Like with many other functional languages, lists are used to solve all kinds of
problems and are undoubtedly the most used data structure in Erlang. They are
analogous to arrays in other, imperative, languages. The basic structure of a
list is [Element1, Element2, ..., ElementN] and you can mix more than one type
of data in it:
1> [1,2,3,4,5].
[1,2,3,4,5]
2> [2, false, "String", {myatom, [list, <<"of">>, [115,116,117,102,102]]}].
[2,false,"String",{myatom,[list,<<"of">>,"stuff"]}]
Remember that strings are actually just lists of
chars.
17. Lists (cont’d)
You can also perform mathematical and comparison operations on strings.
In the above example, you can see that lists are right associative (lines 3 and
4). Line 5 shows that when comparing lists, erlang starts from the first element
and continues until the first differing element. In this case, element4 in the first
list is greater than element4 in the second list.
1> [1,2,3,4,5] ++ [6,7].
[1,2,3,4,5,6,7]
2> [1,2,3,4,5] -- [2,3].
[1,4,5]
3> [1,2,3,4,5] -- [1,2,3] -- [3].
[3,4,5]
4> [1,2,3] ++ [4,5,6] -- [4,5].
[1,2,3,6]
5> [1,2,3,4,5] > [1,2,3,1,200].
true
18. Pattern Matching
Pattern matching is one of the most powerful constructs within Erlang. As you
become familiar with it, you come to love it. I promise! We will use pattern
matching extensively over the remainder of this presentation, here are a few
simple examples:
1> X = 2.
2
2> Y = X + 2.
4
3> {X, Y} = {2, 4}.
{2,4}
4> {X, Y} = {1, 4}.
** exception error: no match of right hand side value {1,4}
5> GoodResult = {ok, "Success"}.
{ok,"Success"}
6> BadResult = {error, "Not success"}.
{error,"Not success"}
7> {ok, Value} = GoodResult.
{ok,"Success"}
8> Value.
"Success"
9> {ok, Value} = BadResult.
** exception error: no match of right hand side value {error,"Not success"}
19. Functions
Functions are first-class citizens in Erlang as in all functional languages.
You can also pass a function as an argument to another function.
1> Factor = 2.
2
2> Double = fun(X) -> Factor * X end.
#Fun<erl_eval.6.80484245>
3> Double(2).
4
1> AddOne = fun(X) -> X + 1 end.
#Fun<erl_eval.6.80484245>
2> Calculate = fun(Num, Fun) -> Fun(Num) end.
#Fun<erl_eval.12.80484245>
3>
3> Calculate(4, AddOne).
5
4> Calculate(4, fun(X) -> math:pow(X, 2) end).
16.0
Anonymous function
20. Modules
Modules in Erlang are somewhat analogous to objects in OOP. However,
Erlang is NOT object-oriented! It may be better to say that modules are a
grouping of, preferably, similar functions in a given file.
In an Erlang module you can declare attributes (metadata describing the
module itself such as its name, public functions, the author, etc.) and functions.
To declare a module you must first give it a name. The name of the module
MUST match the name of the physical file that contains it.
Declaration of ./src/my_first_module.erl
-module(my_first_module).
21. Modules
Now you have a valid module. It doesn’t do anything. But, if you save that file, it
will compile. You won’t be able to do anything with it. You first need to add
some functions. Let’s add a simple function.
This doesn’t really do anything exciting. However, it does display how you
define a method in erlang. First comes the method name. method names follow
the same general rules as atoms. (They must start with a lowercase letter and
only contain alphanumeric characters and underscores.)
say_hello() ->
io:format("Hello!").
22. Modules
Before we can do anything with this module, we need to make that function
publicly available. You do that with an export attribute. The signature of the
export is “-export([method_name/arity])”
Now you have exported that function and it can be used by other modules.
● Calling a module takes the form module_name:function_name(Arguments)
-export([say_hello/0]).
1> c(my_first_module).
{ok,my_first_module}
2> my_first_module:say_hello().
Hello!
ok
23. Recursion
One aspect of erlang that is often difficult to grasp for people coming from an
imperative background is the fact that there is no real concept of a loop. In it’s
place, you use recursive functions. So, instead of using something like this:
You would instead use a recursive function that takes the “head” element of the
list passed-in and prints it:
(Note that the above also introduces the concept of pattern matching on lists.)
for (int i = 0; i < list.length; i++) {
System.out.println(list[i]);
}
print_my_list([]) ->
ok.
print_my_list([H|T]) ->
io:format("~p~n") [H]),
print_my_list(T).
24. Processes
Erlang has been designed from the ground-up to be highly concurrent. A major
part of how it supports this level of concurrency is the fact that all
running/loaded modules are individual processes. you can think abstractly of a
process like a thread in other languages. However, erlang processes are
designed to be short-lived and spun-up and torn-down regularly to perform
isolated processes. As such, Erlang processes have been designed to be much
more lightweight than threads in other languages (~512b vs. ~512MB for a java
thread). It is not unusual to see an Erlang application running 10’s of thousands
of processes simultaneously and still maintaining a relatively small memory
footprint.
25. Processes
Every process is assigned a Process Identifier (PID) that is used to
communicate between Erlang processes. You can get the PID of the current
process with the self() function.
That three section binary is the PID. There is an excellent description of of how
they are constructed with links to the source that creates them here.
(meetup1@dnvcoml-1cydkq4)1> self().
<0.38.0>
26. Processes
You communicate between process via message passing. The send operator
in Erlang is the bang (“!”) symbol. And you send a message by specifying the
PID you want to send the message to followed by the bang then the message
you want to pass.
Now we should get this process communicating to another. To do this, I will
create another shell called meetup2. And, to make things cleaner, I will register
both shell processes with the name of shell.
(meetup1@dnvcoml-1cydkq4)2> self() ! "Hello to me!".
"Hello to me!"
(meetup1@dnvcoml-1cydkq4)3> flush().
Shell got "Hello to me!"
ok
(meetup2@dnvcoml-1cydkq4)1> register(shell, self()).
27. Processes
Now you can easily pass messages between the two shell processes. In this
scenario, you will use a pattern of {process, node} ! message.
(meetup1@dnvcoml-1cydkq4)2> {shell, 'meetup2@dnvcoml-1cydkq4'} ! "Hello meetup2!".
"Hello meetup2!"
(meetup2@dnvcoml-1cydkq4)2> flush().
Shell got "Hello meetup2!"
28. Processes
To receive messages in a process and handle them, you use what is called a
receive loop. Below is a simple example.
Now, if I send a message to meetup2 from meetup1, I will get a response back.
(meetup2@dnvcoml-1cydkq4)1> receive
(meetup2@dnvcoml-1cydkq4)1> {hello, PID} -> PID ! "Right back at ya!";
(meetup2@dnvcoml-1cydkq4)1> {goodbye, PID} -> PID ! "Goodbye! Sorry to see you go!";
(meetup2@dnvcoml-1cydkq4)1> _ -> ok
(meetup2@dnvcoml-1cydkq4)1> end.
(meetup1@dnvcoml-1cydkq4)3> {shell, 'meetup2@dnvcoml-1cydkq4'} ! {hello, self()}.
{hello,<0.38.0>}
(meetup1@dnvcoml-1cydkq4)4> flush().
Shell got "Right back at ya!"
(meetup1@dnvcoml-1cydkq4)6> flush().
Shell got "Goodbye! Sorry to see you go!"