Functions

The Modelverse distinguishes three types of function. For the modeller, it is transparent which type the function is, although it is not possible to dig down in primitive functions. We now present these three types of functions.

Primitive functions

The primitive functions are functions that are implemented directly for the platform, such as in Python. While the function is written in that language, there are still various bindings with the Modelverse, as the function makes the requested changes in the MvS directly. As such, the function also makes use of the yield infrastructure presented previously.

For example, the integer_addition is one such function, which must be implemented in the platform. Its definition is as follows:

def integer_addition(a, b, **remainder):
    if 'value' not in a:
        a['value'], = yield [("RV", [a['id']])]
    if 'value' not in b:
        b['value'], = yield [("RV", [b['id']])]
    yield [("RETURN", [{'value': a['value'] + b['value']}])]

We see that there are two parameters: a and b. Additionally, a dictionary of other parameters is also passed, although these are not necessary most of the time. All alphabetic parameters are those originally passed to the function in the action language, and are represented as a dictionary containing a value and id key. The value key contains the value of the node, stored in a representation that is compatible with the platform. The id key stores the id of the element in the Modelverse. When a value is defined without an id, this means that the value was never stored in the Modelverse to begin with. When an id is defined without a value, this means that the value of the ID was never read out of the Modelverse.

It is easy to see that we can switch between both. To create a value if only an id is present, we read the node:

a['value'], = yield [("RV", [a['id']])]

To create an id if only a value is present, we create the value in the Modelverse:

a['id'], = yield [("CNV", [a['value']])]

As such, the primitive functions will have to check which of the two representations is there, and if the required one is not there, it must be converted. This explains the first four lines of the function: the function checks whether the value is already read from the Modelverse, and reuses it where possible. Finally, we return an element of similar structure as the input values. Again, it is possible for us to just return a value entry, meaning that the value is not stored in the Modelverse yet. It is every functions responsibility to check for the correct type of input, and the output function should only contain either value or id.

The primitive functions are hardcoded in the tool, and have no explicitly modelled counterpart. They are essentially to be seen as part of the specification. While a handle to these functions exists in the Modelverse, when the function is invoked, the instruction pointer is merely pointing at an empty node. During bootstrapping, however, this node was bound to the primitive python function. As this is the only option for these functions, all these functions MUST be ported if the Modelverse is to be ported to a different domain.

Compiled functions

In addition to the primitive functions, which must be implemented directly for the platform, some functions can also be precompiled by the user for performance reasons. Essentially, these functions are the same as the primitive functions, but their definition is stored in kernel/modelverse_kernel/compiled.py. The definition of these functions is identical, and they are automatically detected at startup. Nonetheless, they differ in that a Modelverse definition must also exist, which must have identical behaviour to the implemented function. When such a function is executed in the Modelverse, it is up to the Modelverse to decide which version is to be used. In case the platform provides a pre-compiled function, that one is normally used, otherwise the action language is directly executed.

Through its JIT compiler, the Modelverse actually dynamically creates compiled functions for the platform all the time. As such, all functions are actually written out as platform code, and only sporadically is an actual piece of action language still directly executed.

Normal functions

All other functions are normal functions, but as mentioned before, these functions are often even compiled behind the scenes by the JIT compiler.