Packets

The top-level data structure in ADAPT is a packet. A packet contains a collection of data and an associated MUFL application that implements state transitions of the packet.

Packets are created and initialized by supplying a MUFL application and an initial seed to the CreatePacket method. Subsequent to initialization, the packet is always governed by the code of the application with which it was initialized. The ADAPT environment invokes a packet’s state transition functions (called transactions), which are defined in the MUFL application associated with the packet.

For more information, refer to the trn statement.

All variables defined outside of functions by the associated application or any libraries it loads become data items in the packet. Mutating such a variable in MUFL has the effect of associating a new value with the variable as the result of the state transition being invoked.

Packets are identified by packet IDs and their individual states are identified by packet state IDs. Packet IDs are used as network addresses. They persist (unchanged) across state transitions.

Coming soon: features that enable safe upgrading of MUFL packages to new application versions.

Domains

Data in MUFL is classified into domains. Each domain represents a specific set of values defined by its specific logic. Examples of domains include integers, strings, and dictionaries.

Dictionaries

Dictionaries in MUFL are foundational to data storage and organization. Dictionaries are the ordered key-value maps that can be used to represent tables, lists and so on.

Dictionaries as Arrays and Sets

Other than packets, dictionaries are the only composite data structure in MUFL. Dictionaries are used to represent both arrays (which are treated as dictionaries with integer keys) and sets (which are treated as dictionaries with boolean values).

Immutability

All data in MUFL is immutable. At the top level, invoking a transaction on a packet in a given state results in the creation of a new packet state, after applying the state transition. Similarly, updating a dictionary results in the creation of a new dictionary.

The appearance of data mutations via the -> mutation operator is achieved by reassigning a new immutable value to a name in the scope of a given MUFL namespace (that is, library, module, or application). If a packet P contains a dictionary named some_table, then mutating some_table (which is done in the context of a transaction) indicates that the new state of P will use the new value of some_table. Similarly, if a dictionary some_table has a key "Hello", then mutating some_table "Hello"->new_value reassigns some_table with a new dictionary associating the key "Hello" with new_value.

Hashing

All data in MUFL is hashable using the primitive function _value_id. All hashes refer to data contents and uniquely identify the specific value. Values that are identical are guaranteed to have identical hashes irrespective of how they were constructed, while values that are different are guaranteed with overwhelmingly high probability to have different hashes.

Booleans

MUFL has three special values used to check various conditions in the program: TRUE, FALSE and NIL. TRUE and FALSE serve as ordinary Boolean values that appear as the result of comparisons and predicate operators. NIL is a special value that represents a missing item in a dictionary. In if-statements both FALSE and NIL act the same way: to trigger the else-condition of the statement. TRUE and any other value other than FALSE and NIL trigger the then-condition of the if-statement.

The Boolean type is represented by type expression true+

Data Ordering and Dictionary Keys

Most MUFL data can have the property of being ordered. Ordered data refers to any data that can be used as a key into a dictionary. Even dictionaries can be ordered using their hash values (_value_id), that is, dictionaries can be keys into other dictionaries.

Not all data in MUFL is ordered (for example, functions are not).

Functions as Data

MUFL is a functional language, so it treats functions as data (in functional parlance, the concept of a lambda). Value capture mechanics in MUFL are slightly different from other functional languages. Global variables are captured by name, while local variables are captured by value.

This approach enables functions to have what appears to be a side effect of modifying a variable in the global scope. However, this apparent modification is slightly misleading because all data, including packets, is immutable. In practice, what happens is that all functions in MUFL implicitly take packet state as input and implicitly return packet state as output, enabling MUFL functions to construct new states with very little additional syntactic overhead.