Jim Cheung

Learn You Some Erlang for great good!

notes from Learn You Some Erlang for Great Good!

Chapter 1, Starting Out

Shell

enter erlang shell: $ erl

it uses emacs keybindings, help(). for help. q(). to exit

there could be many shell instances running, enter ctrl-g and h to see a list of commands to operate different shell instances.

so to rescue a frozen shell, enter the followings ctrl-g, i (interrupt job), c (connect job)

commands must end with . and a whitespace ( blank,tab,newline or comment. see Joe Armstrong's answer here )

1> help()
1> help().
* 2: syntax error before: help
1> help(). 
... help contents ...
true
2>

Variables

erlang variables begin with an uppercase letter

erlang variables are immutable, can be only assigned once.

= operator is not just assign/bind values, it compares values.

when variable on the left side is unbound, erlang will bind the value on the right hand side to it then compares left and right.

1> One.
* 1: variable 'One' is unbound
2> One = 1.
1
3> One = One + 1.
** exception error: no match of right hand side value 2
4> One = 2.
** exception error: no match of right hand side value 2
5> One.
1
6>

Atoms

atoms are literals, they're just constants whose only value is their own name.

atom should be enclosed in sisngle quotes ' if it does not begin with a lower case or contains any characters other than alphanumeric characters, _ or @.

atom is referred to in an atom table, which consumes memory. (4 bytes per atom in a 32-bit system and 8 bytes in 64-bit system).

the atom table is not garbage collected, so avoid dynamic generation of atoms.

Boolean Algebra and Comparison Operators

and and or will always evaluate arguments on both sides.

use andalso and orelse if you want to evaluate the right-side argument only if necessary.

always uses =:= and =/= to test first, use == and /= when you do not need exactly equality.

erlang has no such things as Boolean true and false, true and false are atoms:

1> 0 == false. 
false

Tuples

tuple is written in the form {Element1, Element2, ..., ElementN}

1> X = 10, Y = 4. 
4
2> Point = {X,Y}. 
{10,4}

the _ variable is always seen as unbound and acts as a wildcard for pattern matching.

a tuple that contains an atom with one element following it is called a tagged tuple:

1> {point, {X,Y}}. 
{point, {4,5}}

Lists

basic notation of a list is [Element1, Element2, ..., ElementN], and you can mix more than one type of data in it:

1> [1, 2, 3, {numbers,[4,5,6]}, 5.34, atom].
[1, 2, 3, {numbers,[4,5,6]}, 5.34, atom]

strings are lists, erlang will print lists of numbers as numbers only when at lease one of them could not also represent a letter.

1> [97, 98, 99].
"abc"
2> [97,98,99,4,5,6].
[97,98,99,4,5,6]

there is no such thing as a real string in erlang. (there is another way to write strings, see Binary Strings)

to glue lists together, use ++, to remove use --:

1> [1,2,3] ++ [4,5].
[1,2,3,4,5]
2> [1,2,3,4,5] -- [1,2,3].
[4,5]

both ++ and -- are right-associative:

1> [1,2,3] -- [1,2] -- [3].
[3]
2> [1,2,3] -- [1,2] -- [2].
[2,3]

the first element of a list ia named the head, the rest is named the tail:

1> hd([1,2,3,4]).
1
2> tl([1,2,3,4]).
[2,3,4]

you can use [head|tail] pattern matching:

1> List = [2,3,4].
[2,3,4]
2> NewList = [1|List].
[1,2,3,4]
3> [Head|Tail] = NewList.
[1,2,3,4]
4> Head.
1
5> Tail.
[2,3,4]

the | is called the cons operator (constructor), any list can be built with the formula [Term1 | [Term2 | [... | [TermN]]]

using the form [1 | 2] gives an improper list.

improper list will work when pattern match in the [Heal|Tail] manner, but will fail when used with standard functions of erlang, like length()

List Comprehensions

the recipe for list comprehensions in erlang is as follows:

NewList = [Expression || Pattern <- List, Condition1, Condition2, ... ConditionN]

the Pattern <- List part is called a generator expression.

1> [2*N || N <- [1,2,3,4]].
[2,4,6,8]
2> [X || X <- [1,2,3,4,5,6,7,8,9,10], X rem 2 =:= 0].
[2,4,6,8,10]
3> RestaurantMenu = [{steak, 5.99}, {beer, 3.99}, {poutine, 3.50}, {kitten, 20.99}, {water, 0.00}].
[{steak,5.99},
 {beer,3.99},
 {poutine,3.5},
 {kitten,20.99},
 {water,0.0}]
4> [{Item, Price*1.07} || {Item,Price} <- RestaurantMenu, Price >= 3, Price =< 10].
[{steak,6.409300000000001},{beer,4.2693},{poutine,3.745}]

you can have more than one generator expression:

5> [X+Y || X <- [1,2], Y <- [3,4]].
[4,5,5,6]

Working with Binary Data

erlang bit syntax encloses binary data between << and >>

1> Color = 16#F09A29.
15768105
2> Pixel = <<Color:24>>.
<<240,154,41>>

<<Pattern, Rest/binary>> is like [Head|Tail]:

8> <<R:8, Rest/binary>> = Pixels.
<<213,45,132,64,76,32,76,0,0,234,32,15>>
9> R.
213

parse TCP segments:

<<SourcePort:16, DestinationPort:16,
AckNumber:32,
DataOffset:4, _Reserved:4, Flags:8, WindowSize:16,
CheckSum: 16, UrgentPointer:16,
Payload/binary>> = SomeBinary.

Binary String

binary string is much more efficient in terms of space.

<<"this is a binary string">>

use binary strings when storing text that won't be manipulated too much or when space efficiency is a real issue.

atom is more suitable for tag values, comparisons are done in constant time regardless of length.

Conversely, strings can be manipulated (splitting, regular expressions, etc) while atoms can only be compared and nothing else.

Binary Comprehensions

1> [ X || <<X>> <= <<1,2,3,4,5>>, X rem 2 == 0].    
[2,4]

The only change in syntax from regular list comprehensions is the <- which became <= and using binaries (<<>>) instead of lists ([])

2> Pixels = <<213,45,132,64,76,32,76,0,0,234,32,15>>.
<<213,45,132,64,76,32,76,0,0,234,32,15>>
3> RGB = [ {R,G,B} || <<R:8,G:8,B:8>> <= Pixels ].
[{213,45,132},{64,76,32},{76,0,0},{234,32,15}]

Changing <- to <= let us use a binary stream as a generator.

Chapter 2, Modules

Modules are a bunch of functions regrouped in a single file, under a single name.

When writing a module, you can declare two kinds of things: functions and attributes.

All module attributes follow the form -Name(Attribute).

-module(Name).

This is always the first attribute (and statement) of a file.

the name of the module and the filename must match.

-export([Function1/Arity, Function2/Arity, ..., FunctionN/Arity]).

for development/debug, add this attribute to export all functions:

-compile([debug_info, export_all]).

Erlang programmers are often discouraged from using the -import attribute.

Compiling Code

$ erlc flags file.erl

or in the erlang shell:

compile:file(filename).

c(Name).

Erlang functions and expressions must always return something,

To compile to native code, you need to use the hipe module:

hipe:c(Module,OptionsList).

or

c(Module,[native]).

the .beam file generated will no longer be portable across platforms.

Defining Macros

Erlang macros are mainly used to define short functions and constants.

-define(MACRO, some_value).

-define(sub(X,Y), X-Y).

then use the macro as ?MACRO.

there are few predefined macros: ?MODULE, ?FILE, ?LINE

use attribute ifdef(MACRO)., -else.. and -endif. to check macros are defined:

-ifdef(DEBUGMODE).
-define(DEBUG(S), io:format("dbg: "++S)).
-else.
-define(DEBUG(S), ok).
-endif.

-ifdef(TEST).
my_test_function() ->
    run_some_tests().
-endif.

to define macros in compile function:

c(Module, [{d, 'TEST'}, {d, 'DEBUGMODE'}]).

More about modules

most module attributes are stored in a module_info/0 function.

module_info/1 lets you grab one specific piece of information.

vsn is an automatically generated unique value differentiating each version of your code, excluding comments.

You can also specify a vsn value by adding -vsn(VersionNumber) to your module.

Chapter 3, Syntax in Functions

Chapter 4, Types (or Lack Therof)

Chapter 5, Hello Recursion!

Chapter 6, Higher-Order Functions

Chapter 7, Errors and Exceptions

Chapter 8, Functionally Solving Problems

Chapter 9, A Short Visit to Common Data Structures

Chapter 10, The Hitchhiker's Guide to Concurrency

Chapter 11, More on Multiprocessing

Chapter 12, Errors and Processes

Chapter 13, Designing a Concurrent Application

Chapter 14, An Introduction to OTP

Chapter 15, Rage Against the Finite-State Machines

Chapter 16, Event Handlers

Chapter 17, Who Supervises the Supervisors?

Chapter 18, Building an Application

Chapter 19, Building Applications the OTP Way

Chapter 20, The Count of Applications

Chapter 21, Release Is the Word

Chapter 22, Leveling Up in the Process Quest

Chapter 23, Buckets of Sockets

Chapter 24, EUnited Nations Council

Chapter 25, Bears, ETS, Beets: In-Memory NoSQL for Free!

Chapter 26, Distribunomicon

Chapter 27, Distributed OTP Applications

Chapter 28, Common Test for Uncommon Tests

Chapter 29, Mnesia and the Art of Remembering

Chapter 30, Type Specifications and Dialyzer