When thinking of immutable state, I imagine a large
input->[processingbox]->output
box being broken into a chain of multiple smaller mini-boxes,
input0->[pbox0]->output0 -> [pbox1]->output1 ->[pbox2]->output3
with each individual box accomplishing a change in its input to its output, without keeping any internal mutable state.
Which reminds me of Linear-Time-Invariant systems. As long as each part of the system is linear and time-invariant, a large system can be composed of simpler parts and still be analyzed by deriving a composite transfer function. The composite system can be studied for its stability and loss/gain characteristics and adjustments made in the system design.
I suspect a similar payoff occurs with functional programming. But it is not so clearly stated or visible in the ability to analyze large programs. Erlang is essentially a version of event driven programming. The conciseness of expression is encouraging, but I hope to arrive at good examples of program composition.
Meanwhile the ‘if’ statement in erlang is a curveball. In addition to the odd syntax, and statements like true->false, one finds one cannot place a log statement anywhere inside a set of statements to see what’s going on. Punctuation rules. The trick is to place the log a comma before the last expression before the semicolon.
if Val >= Left andalso Val =< Right -> io:format("==> ok~p<~p<~p\n\n",[Left,Val,Right]), true; true -> io:format("==>not ok ~p<~p<~p\n\n",[Left,Val,Right]), false end.
The semicolons indicate different phrases which are pattern-matching options. The commas are continued statements within the same phrase. The last statement in the phrase is the return value. The last phrase is typically a catch-all, in this case returning a value of false. The case statement is more commonly used than the if. The -> denotes ‘lhs condition is mapped to rhs’ .
The ‘for’ statement does not exist at all and below are alternatives.
foreach(Fun, List)->ok, %% just applies a function Fun to each element of a list foldl(Fun, Accumulator, List)-> Acc1, %% fold list left to right into an accumulator foldl(Fun, Accumulator, List)-> Acc1 %% fold list right to left into an accumulator map(Fun, List1) -> List2 %% maps a list to a new list
The = sign in erlang is used for (a) immutable assignment of a term to a value and (b) for comparing if a previously assigned term or expression of terms is the same as another term or value – this is called pattern matching. One cannot reassign a term to a new value, so the math notion of equality always holds. The = used in C/Java could be called mutable assignment, every variable is hiding within it mutable state. A term in erlang does have an internal state of bound or unbound. On a successful pattern match any unbound variable becomes bound.
1> [H|T] = [1,2,3].
2> io:write(H) .
To generate a list of numbers, like range(n) in python, there is lists:seq(1,n). io:fwrite(“~p”, [lists:seq(1,20)]).
The philosophy behind some of these decisions is discussed here https://news.ycombinator.com/item?id=13499377. An objective is to make it impossible to write runaway loops – to make the runtime system always be able to recover and reallocate its cycles. A related aspect is to let-it-fail and not write a lot of exception handling, defensive code.