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.
