Learn You Some Erlang for great good!
notes from Learn You Some Erlang for Great Good!
- Chapter 1, Starting Out
- Chapter 2, Modules
- 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
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.