Sequential Erlang

The basic constructs of the Erlang language.

Datatypes

Integers

  • Large Integers are converted to bignums

If you need to do calculations on integers using a base other than 10, you can use Base#Value notation. (Laurent 2017, 7)

Floats

  • When you divide two integers with /, the result is automatically converted to a floating-point number.
  • Integer division is handled by div and rem.

Atoms

  • In Erlang, atoms are used to represent constant values.
  • Atoms are also global, and this is achieved without the use of macro definitions or include files.

Tuples

  • Represents a collection of elements (of any type) that are grouped together.
  • Access is normally done by position.
  • A tuple whose first element is an atom is called a tagged tuple, i.e., {book, "The Aleph"}.

Tuples are created automatically when we declare them and are destroyed when they can no longer be used. Erlang uses a garbage collector to reclaim all unused memory, so we don’t have to worry about memory allocation. (Armstrong 2013, 35)

You can also pattern match tuples by using free variables:

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

Lists

  • A list is a compound data type with a variable number of terms: [Term1,...,TermN].
  • Access is normally done by parttern matching.
  • One can add or iterate over lists with the cons operator |, which breaks a list into H|T (head and tail),
  List = [ Element | List ] OR []

List Comprehensions

  1> L = [1,2,3,4,5,6,7].
  [1,2,3,4,5,6,7]
  2> [ 2*X || X <- L ].
  [2,4,6,8,10,12,14]
  3> 

The most general form of a list comprehension is an expression of the following form: [X || Qualifier1, Qualifier2, ...], where X is an arbitrary expression, and each qualifier is either a generator, a bitstring generator, or a filter.

  • Generators are written as Pattern <- ListExpr where ListExpr must be an expression that evaluates to a list of terms.
  • Bitstring generators are written as BitStringPattern <= BitStringExpr where BitStringExpr must be an expression that evaluates to a bitstring.
  • Filters are either predicates or boolean expressions. (Armstrong 2013, 61)

Strings

Strictly speaking, there are no strings in Erlang. To represent a string in Erlang, we can choose between representing the string as a list of integers or as a binary. When a string is represented as a list of integers, each element in the list represents a Unicode codepoint.

To print a unicode string one must use the "t" modifier applied to the "s" control character in a formatting string, it accepts all Unicode codepoints and expect binaries to be in UTF-8:

  1> X = "a\x{221e}b".
  [97,8734,98]
  2> io:format("~ts~n",[X]).
  a∞b
  ok

Records

(…) records provide a convenient way for associating a tag with each of the elements in a tuple. This allows us to refer to an element of a tuple by name and not by position. A pre-compiler takes the record definition and replaces it with the appropriate tuple reference. (Armstrong 2013)

  -record(todo, {status=reminder,who=joe,text}).

to load a record from the the shell, one must use the rr command:

  1> rr("records.hrl").
  [todo]
  2> #todo{}.
  #todo{status = reminder,who = joe,text = undefined}
  3> X1 = #todo{status=urgent, text="Fix errata in book"}.
  #todo{status = urgent,who = joe,text = "Fix errata in book"}
  4> X2 = X1#todo{status=done}.
  #todo{status = done,who = joe,text = "Fix errata in book"}

Maps

Maps are associative collections of key-value pairs.

  1> TaskPending = #{ status => pending, description => 'feed cats' }.
  #{status => pending,description => 'feed cats'}
  2> TaskDone = TaskPending#{ status := done }.
  #{status => done,description => 'feed cats'}

Binaries

A sequence of bits that can be pattern matched.

Identifiers

Pids

Ports

A file descriptor that can be used to communicate with external programs.

References

A unique reference created by the make_ref BIF.

Variables

Note that Erlang variables start with uppercase characters. So, X, This, and A_long_name are all variables. Names beginning with lowercase letters—for example, monday or friday are not variables but are symbolic constants called atoms. (Armstrong 2013)

  • Erlang Variables Do Not Vary
  • The scope of a variable is the lexical unit in which it is defined.
  • Variables acquire values as the result of a successful pattern matching operation (=).

Pattern Matching

Pattern matching is used for:

  • Assigning values to variables
  • Redirecting execution flows

case and if Expressions

  case Expression of
    Pattern1 [when Guard1] -> Expr_seq1;
    Pattern2 [when Guard2] -> Expr_seq2;
    ...
  end

  if
    Guard1 -> Expr_seq1;
    Guard2 -> Expr_seq2;
    ...
  end

Functions

Built-in Functions

  • Conventionally, these are part of the erlang module.
  • Mostly written in C for fast execution.

List of BiFs

  • date()
  • time()
  • length(List)
  • size(Tuple)
  • atom_to_list(Atom)
  • list_to_tuple(List)
  • integer_to_list(1000)
  • tuple_to_list(Tuple)

Funs: The Basic Unit of Abstraction

Funs are function closures. Funs are created by expressions of the form: fun(...) -> ... end.

Defining Your Own Control Abstractions

If we want additional control structures, we can make our own. Erlang has no for loop, so let’s make one:

  for(Max, Max, F) -> [F(Max)];
  for(I, Max, F) -> [F(I)|for(I+1, Max, F)].

Recursion

Tail Recursion

Guards

Modules

Defining Modules

    -module(drop).
    -export([fall_velocity/1, mps_to_mph/1, mps_to_kph/1]).

    fall_velocity(Distance) -> math:sqrt(2 * 9.8 * Distance).
    mps_to_mph(Mps) -> 2.23693629 * Mps.
    mps_to_kph(Mps) -> 3.6 * Mps.

Error Handling in Sequential Programs

  try FuncOrExpressionSeq of
    Pattern1 [when Guard1] -> Expressions1;
    Pattern2 [when Guard2] -> Expressions2;
    ...
  catch
    ExceptionType1: ExPattern1 [when ExGuard1] -> ExExpressions1;
    ExceptionType2: ExPattern2 [when ExGuard2] -> ExExpressions2;
    ...
  after
    AfterExpressions
  end
exit/1
Used to terminate the current process.
throw
Used as a documentation to the caller, to show that a function might throw this exception.
error
Crashing errors.

Fail Fast and Noisily, Fail Politely

In Erlang, when an error is detected internally by the system or is detected by program logic, the correct approach is to crash immediately and generate a meaningful error message.

(…)

Second, fail politely means that only the programmer should see the detailed error messages produced when a program crashes. A user of the program should never see these messages.

References:

Armstrong, Joe. 2013. “Programming Erlang: Software for a Concurrent World.”
Laurent, Simon St. 2017. Introducing Erlang: Getting Started in Functional Programming. O’Reilly Media, Inc.

Backlinks: