No set of essential languages would be complete without a functional programming language, and Haskell is a very good example of this paradigm. As much as possible, functional languages dispense with the idea of global state. Unlike a procedure, a pure function will always return the same value when called with the same arguments; its result doesn’t depend on anything other than the arguments.
Like Erlang, Haskell is a language closely related to a theoretical model for computation. In this case, the model is ë-calculus. The operations in Haskell map directly to the reductions permitted by ë-calculus, making the language very popular with people interested in formal verification. You can easily map between a Haskell program and a set of ë-calculus terms, so anything you can prove about the terms also applies to the program.
For the most part, functions are not allowed to have side effects, and those that do are easy for the compiler to spot. If a function doesn’t have side effects, then the only constraint on when it can be executed is when you use the return value. If you never use the return value, you can avoid evaluating it at all (lazy evaluation). This property allows you to extract parallelism implicitly from functional programs. Consider the following pseudocode:
a = foo() b = bar()
In a procedural language, foo() might alter some global state, and so the outcome of bar() might depend on foo(). In a pure functional language, this isn’t the case, so foo() and bar() can be executed concurrently.