Mix is a hybrid language and unifies both functional and imperative programming in a single syntax. However, both aspects are clearly separated and backed by specific notations, and the strictness of this may be a bit surprising.
Expressions are composed of operator applications (like x+y), function applications (like f(x) or x |> f), function abstractions (like (x -> expr)), and a few additional constructions:
if/else (note that elseis mandatory inside expressions - and also note that in expresions if is written without curly braces)letvarwith notationYou can define functions and cells as expressions like:
def f1(x,y) = 2*x + 3*y
def f2 = ((x,y) -> 2*x + 3*y)
cell c1 = if (c2 > 0) 10 else c3 + 5
cell d1 = let x = "foo"; { bar: x }
The essence of an expression is that it has a result.
An expression is not necessarily total, because evaluating an expression may lead to runtime errors, which will cause the current coroutine to be killed. You can force a runtime error in an expression with fail("message"). Note that runtime errors cannot be caught without the help of the coroutine machinery.
The result of an expression can also be undefined - which is a very special value (e.g. it interacts specially with comparisons and logic, and often bubbles up, and cannot be put into containers).
We aim at ensuring that expressions do not have effects, but note that at the moments there are some loop-holes in the language that still allow it. An “effect” is here mainly meant in the sense of a mutation of a variable or var cell (there are other definitions of “effect” in the literature).
An expression has a fixed evaluation order. For example, we only evaluate the “then” part of an if when the condition is true. For example, a let variable is set before the expression is evaluated using it. This is important for evading runtime errors.
There are a couple of constructions that are not allowed:
return: you cannot jump out of an expression. The result of an expression is always the last computed value.for and while: if you want to program loops, you have to use functional programming tools (like recursion or reductions)if lacking else: because an expression must have a result, the else branch must existif with curly braces: the curlies are seen as an indication for programming with statements (see below), and this is opposed to programing with expressionsswitch is not directly possible, but there is an alternate notation using functions with pattern matching, e.g. x |> (pat1 -> expr1 | pat2 -> expr2 | ...)Code can alternatively also be written with statements. In Mix, statements are always grouped together with curly braces, and the statements of the group are separated with semicolons, e.g.
{
var x = 0;
x = x + 1;
return x;
}
The statements can themselves be:
letvarreturnif with curly braces (and else is optional)while and for loopsswitchThe essence of a statement is that it has an effect.
It is a different composition principle. Expressions are composed from simpler expressions by taking the result of the latter and combining them to the final result. In contrast, statements are executed in sequence and mutate variables during that - later statements pick up effects made by earlier statements.
You can write functions and cells with statements, e.g.
def f1(x,y) { return 2*x + 3*y }
def f2(x,y) { var acc = 2*x; acc = acc + 3*y; return acc }
cell c1 { if (c2 > 0) { return 10 }; return c3 + 5 }