Reductions

Reduction takes a function or a data structure and produces a result.

A is int->>int = (,). // A is an empty dictionary
A 2->3. // Mutation: A is now a dictionary (2->3)
_print (A 2). // Reduction: this will print "3"

Prints:

3

Reductions with Default

It is often convenient to ensure that a collection, such as a map or an array, has appropriate elements before retrieving an element as part of an operation.

For example, consider this code:

PeoplesList is str->>($first_name->str, $last_name->str, $city->str) = (,). 
if (PeoplesList "Alex" == NIL) then 
    PeoplesList "Alex" -> ($first_name->"Alex", $last_name->"", $city->""). 
end

if (PeoplesList "Alex" $last_name == NIL) then 
    PeoplesList "Alex" $last_name -> "Johnson". 
end

Using the keyword default, you can shorten the code significantly:

PeoplesList is str->>($first_name->str+, $last_name->str+, $city->str+) = (,). 
if (PeoplesList "Alex" default ($first_name->"Alex", $last_name->NIL, $city->NIL) $last_name == NIL ) then 
    PeoplesList "Alex" $last_name -> "Johnson". 
end

To summarize, a reduction with a default keyword includes automatically-generated code to mutate the data structure to conform to a certain standard before the reduction takes place, avoiding extra verbose checks and the potential for encountering NIL values.

Low-lexicographic Priority Reduction Expressions

With curry notation, which is left-associative, it is often necessary to use parentheses to separate a reduction in one of the arguments to a higher level reduction, such as in the expression: _assert (test_func 1). As a syntactic convenience, MUFL provides a reduction syntax with low parsing priority by using the << operator:

_assert << test_func 1. // Equivalent to _assert (test_func 1).

In general, a << b << c << d is right-associative and is equivalent to a (b (c d)).

Reduction expressions and arithmetic, when mixed, always require parentheses to avoid confusion:

(a b) + 1. // valid
a (b + 1). // valid
a b + 1. // invalid

Mutations

The statement x->y mutates the value named by x to become equal to y at the next state of the packet. x could be any variable, a dereference expression or a reduction expression. In the latter case, the mutation ensures that the dictionaries referred to by the reduction expression x are updated correctly.

For example:

x is int->>int->>int = (,). // empty dictionary of type any
x 1->(,). // add an empty dictionary into x under key 1
x 1 2->3. // add element (2->3) into the new dictionary (x 1)

While it is possible to mutate local variables inside a function (for example, to create a loop counter), it is not possible to construct references to local function-level variables or to mutate local variables of an enclosing function in an enclosed function.

Deletion

The statement delete x is effectively identical to x->NIL. Any expression x allowed in a mutation is allowed in the deletion.