|
@@ -1,26 +1,70 @@
|
|
|
Functions
|
|
|
=========
|
|
|
|
|
|
-Explain the different types of functions in the Modelverse.
|
|
|
+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
|
|
|
-------------------
|
|
|
|
|
|
-Explain primitive functions:
|
|
|
-hardcoded in kernel, no AL equivalent
|
|
|
-minimal set of functions to be ported for new kernel
|
|
|
+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
|
|
|
------------------
|
|
|
|
|
|
-Explain compiled functions:
|
|
|
-hardcoded in kernel, but AL equivalent
|
|
|
-can be ported for efficiency fo the resulting kernel, but not required
|
|
|
+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
|
|
|
----------------
|
|
|
|
|
|
-Explain normal functions:
|
|
|
-completely in AL, no compiled equivalent
|
|
|
-can still be implemented by kernel as compiled for performance, though not required
|
|
|
-functions are likely to be flexible
|
|
|
+All other functions are normal functions, but as mentioned before, these functions are often even compiled behind the scenes by the JIT compiler.
|