Erlang
It is hard to tell, especially to an uninitiated, that Erlang is a both a minimalist and principle-based language - a set of necessary and sufficient features which are orthogonal but complementing to each other.
CLU, Scheme, Standard ML and Haskell are other such languages.
Some syntactic oddities are the primary cause of why people give up too early. Syntax aside, it is otherwise brilliant. The designers has a solid mathematical background and it can be seen.
Unlike SML, Erlang has heterogeneous lists and type-tagged values, just like Common Lisp or Scheme.
Just like Haskell and lately Python, Erlang has purely declarative list comprehensions, which can be traced back to the mathematical notation, developed within the Set Theory (the basis for all mathematics).
Being declarative with a high-level DSL is the highest possible level of excellence. Besides set comprehensions, summations and integrals are examples of such declarative notations.
We, in programming, use DSLs, sometimes as beautiful and close to math as Scala’s scalatest
set of libraries or the original quickcheck
for Haskell.
Erlang lacks some abilities of expressing DSLs due to neglect of syntax. Currying and partial application + the ability to define arbitrary infix operators is what it is lacking.
User-facing subset of Erlang is pure functional. send
is an infix operator and
acts like an identity function. recieve
is like a multi-clause “function” (it is
pure with respect to the pattern and a given context).
spawn
is a pure as apply
.
Just like SML, pattern-matching is the main mechanism of dealing with structured and nested values.
Erlang pioneered universal pattern-matching (first introduced in SML), which means patterns are everywhere.
=
is not an “assignment”, but binds unbound symbols or is an “assertion” for both
sides of =
to have the same shape with exactly the same values. SML uses let
.
Patterns could appear in list comprehensions for both descructuring and constructing.
Function clauses (which SML also have) also defined by pattern-matching. Thus each clause becomes a partial function (defined only on a particular subset of the domain).
Functions with more that one clause are usually defined for algebraic sum-types, and the overall shape of a function follows to the shape of a data-type.
Unlike SML, Erlang does not support currying and partial application.
Pattern-matching on receive
is Erlang’s unique innovation. This allows to
examine the shape of received data, destructure and bind to symbols.
Each receive
clause is a partial “function” with “arguments” being bound by the
pattern-matching process.
the [H|T]
syntax. In a pattern it unpacks and binds, in an expression it
constructs.
We can easily add tests and perform test-driven development without any
additional tools. All we need is /pattern matching and =
.
result (self-evaluating) = expression
this is an implicit assertion about structured values (of the same shape).
Don’t handle the case where no pattern matches—our program will fail with a runtime error. /This is deliberate. This is the way we program in Erlang. (Fail fast!)
tuples
The idea is to add an atom as a tag as the first element of a tuple and pattern-match against such tuples is simply brilliant and just right - it is rational, /principle-guided minimalism.
when guards
These are just expressions of the type true | false
(those are just atoms).
,
is an implicit strictAND
, because all the expressions are going to be evaluated in order.;
is an implicit strictOR
, because the later will be evaluated (in order) when the former fails.andalso
andorelse
are short-circuiting logical operators (evaluation stops on the firstfalse
ortrue
respectively and the second expression will not be evaluated).
Notice how ,
is is the essence of product-type and ;
in this context is just |
of a sum-type.
test(X) when X =:= cat; X =:= dog -> yay % either cat or a dog
The set of standard predicates is a reminiscence of Common Lisp ones, just with a
different naming convention – is_something(x)
.
Let it crash. Fail Fast and Noisily. Crash, Crash, Crash.
The proper way is to expect an “error” in a sum-type of a return value..
Erlang’s way is to return a tuple with {ok, Value}
or {error What}
.
Another way is to throw an exception.
The most important thing is that errors has to be well-understood and expected.
Fault-tolerance requires a supervision hierarchy (redundancy). A supervisor must “know what to do” (usually just restart).