Language

  • Constant or Variable -- begins with upper-case, like variables that you can set exactly once.
  • symbol -- begins with a lower-case letter, you can think of them sort of like enumerations.
  • _ can be used like a constant in that you can assign it to anything, but it always remains undefined. Like /dev/null. {_,_,X,_} = {1,2,3,4}. will set X to 3.
  • _Var -- A variable with a leading _ suppresses warnings. This is useful for debugging when you may be commenting variable uses out, etc.
  • [list] -- lispish list of cells, easy to slice up and recurse over.
  • {tuples} -- flat array, easy to treat as a single unit.
  • [H|T] sets H to the first item in the list, T to the rest of the items in the list. If the list only has one item then T receives [] (the empty list).
  • fun square/1 -- refers to the local square function with arity 1. fun nums:square/1 would be the square function in the nums module.

Operators

  • ++ -- list concatenation. [1,2] ++ [3,4]. produces [1,2,3,4]. ++ is frowned upon though: "it should set alarm bells off in your brain—this is very inefficient and acceptable only if the concatenee is very short."
    • Shortcut in patterns: f("begin" ++ T) -> ... is equivalent to [$b,$e,$g,$i,$n|T] -> ...
  • -- -- list subtraction. [1,2,3,2] -- [2,3]. produces [1,2] (use [2,2,3] to get rid of both 2s).
  • = -- equality. Can be used almost anywhere: {1, {2,3}=X, 4}=Y will set X and Y.
:
#
(unary) +, (unary) -, bnot, not
/, *, div, rem, band, and               - Left Associative
+, -, bor, bxor, bsl, bsr, or, xor      - Left Associative
++, --                                  - Right Associative
==, /=, =<, <, >=, >, =:=, =/=
andalso
orelse

You should almost never use == and /=. These are only useful when comparing integers to floats: 12 == 12.0 but 12 =/= 12.0. Instead, always use =:= and =/=. Pattern matching uses =:= so fun(12) is not the same function as fun(12.0).

List Comprehensions

[X || Qualifier1, Qualifier2, ...] -- X is an arbitrary expression, and each qualifier is either a generator or a filter.

  • Generators are written as Pattern <- ListExpr where ListExpr must be an expression that evaluates to a list of terms.
  • Filters are either predicates (functions that return true or false) or boolean expressions.
  • [ A || A <- [1,2,3,4], A =< 3 ] produces [1,2,3]
  • [ F(X) || X <- L ] means “the list of F(X) where X is taken from the list L.”
  • [ X || {a, X} <- [{a,1},{b,2},{c,3},{a,4},hello,"wow"]]. produces [1,4]

Macros / Preprocessor

Macros are expanded when a ? precedes the name: ?Macro

  • -define(Constant, Replacement)
  • -ifdef(Macro)
  • -ifndef(Macro)
  • -else
  • -endif
  • -undef -- after an undef, the macro can no longer be used.
-ifdef(debug).
-define(TRACE(X), io:format("TRACE ~p:~p ~p~n" ,[?MODULE, ?LINE, X])).
-else.
-define(TRACE(X), void).
-endif.
  • ?FILE -- current filename
  • ?LINE -- current line number in the file
  • ?MODULE -- name of the module
  • -include("Filename") -- includes the named file (typically FILE.hrl)
  • -include_lib(name) -- includes name.erl, name is an atom.
  • -module(name) -- name the module
  • -export([fun/1, fun/2]) -- exports functions from your module.
  • --compile(export_all).

Process Dictionary

Each process has its own dictionary. Don't use it! Key is an atom.

  • put(Key, Value) -> OldValue
  • get(Key) -> Value
  • get() -> [{Key,Value}] -- returns the entire dictionary
  • get_keys(Value) -> [Key].
  • erase(Key) -> Value.
  • erase() -> [{Key,Value}].

Commands

All these commands are defined in the shell_default module.

  • b() -- display all variable bindings. See f().
  • c(file) -- compile the module (module resides in file.erl)
  • c(module, options) -- compile with the given options, c(mymod, {d, debug})
  • f(), f(var) -- forget all variables and start over. useful in the interpreter.
  • h() -- history
  • halt() -- exit the interpreter. This is rather violent -- use q() when you can.
  • q() -- exit the interpreter with an orderly shutdown.
  • rf(), rf(rec) -- forget record
  • rr(file) -- read record, rr("records.hrl"). (can be used for any include file?)

Functions

Guard Built-In functions

  • abs(X)
  • element(pos,tuple)
  • hd(list) -- returns the list head (fasdt, see also tl())
  • length(list) -- length of the list
  • float(X) -- convert X to a float (see also round and trunc)
  • node(), node(X) -- the node on which X was created
  • round(X) -- rounds X to an integer (see also float and trunc)
  • self() -- pid of current process
  • size(X) -- size of X, X can be a tuple or a binary
  • trunc(X) -- truncates X to an integer (see also round and float)
  • tl(X) -- tail of a list (slow right? see also hd())

Predicates

is_atom(X) is_binary(X) is_constant(X) is_float(X) is_function(X) is_function(X, N) (X is a fun with N arguments) is_integer(X) is_list(X) is_number(X) (is_integer(X) or is_float(X)) is_pid(X) is_port(X) is_reference(X) is_tuple(X) is_record(X,Tag) (X is a record of type Tag) is_record(X,Tag,N) (X is a record of type Tag and size N)

Library

Lists:

  • lists:map, lists:sum, lists:filter, lists:seq(lo,hi), lists:reverse

Preprocessor

Somewhat disturbingly, Erlang uses a preprocessor to implement macros. To see the preprocessed output of a file, run compile:file(perms,['P']). (this preprocesses perms.erl and stores the output in perms.P)

Documentation

Type Notation

  • any() (same as term()), atom(), binary(), bool(), char(), cons(), deep_string(), float(), function(), integer(), iolist(), list(), nil(), none() (used, for instance, when a function doesn't return), number(), pid(), port(), reference(), string(), term() (same as any()), tuple().

list() is probably not very useful. Just specify the list directly: [pid(), atom()]

TODO: cons()? Also, I notice people using int(), is that OK too?

Examples:

 @type mode() = read | write.
 @type fileSpec = {name(), size(), mode()}.
 @type name = string().
 @type size = integer().
 or: @type fileSpec = {FileName::string(), FileSize::integer(), mode()}.
 
 @spec stat(Path, fileSpec()) -> {ok, Handle} | {error, Why}.
 @spec lists:map(fun(A) -> B, [A]) -> [B].
 @spec lists:filter(fun(X) -> bool(), [X]) -> [X].

Used by EDoc and Dialyzer.

Places to Find Docs

Random

Quicksort

This is the second coolest quicksort implementation I've ever seen (the coolest is Haskell's). Of course, ++ is bad news in Erlang. Making this fast takes a bit of effort.

  qsort([]) -> [];
  qsort([Pivot|T]) ->
       qsort([X || X <- T, X < Pivot])
       ++ [Pivot] ++
       qsort([X || X <- T, X >= Pivot]).

Benchmarking / Profiling

http://erlane-project.blogspot.com/2007/08/binary-search-ultimate-test.html

Spawning Processes

In Erlang: "every time we call someModule:someFunction(...), we’ll always call the latest version of the function in the latest version of the module, even if we recompile the module while code is running in this module." Wow.

Some catches:

  • You can only have a current version of the module running and one older version. If you're running both, and you compile a new version of the module, the old versions are killed off, current current become old, and the new compile becomes current. To ensure that no processes will be killed off if you recompile, call erlang:check_process_code/2. erlang:purge_module/1 will get rid of old modules (dunno whether it kills processes or not).
  • Let's say that module A:host is spawning a process that executes function B:guest(). Now, let's make changes to B and compile it. Hey! Why does A:host continue to spawn the old B:guests? It's because you need to recompile A:host to get it to refer to the new B:guest. But now we have a problem; chances are, A:host will never exit, making it impossible to ever spawn a new B:guest! Not a problem! If you spawn using spawn/3, which takes MFA (module/function/arguments), the spawn adds a bit of indirection to ensure that the latest compiled copy of the module is used. You name the module and function using atoms -- you can't just pass in an anonymous function.

TODO: erlang:statistics(run_queue) to measure the load of a node.

To Improve

  • Make the shell exactly the same as the compiler/interpreter. Any syntax that works in one should work in the other.
  • String handling is wack
  • Preprocessors are stupid. Transforms should be done on the AST, not on the source code.

Does Erlang garbage collect processes when they're no longer reachable? (It appears that it does! Have to read more)