Stateful functions¶
A stateful function is a piece of ‘native’ code which is sequentially executed by ohua and has for its execution context some associated, opaque state.
Concretely this means that a stateful function, in Java for instance, is a method and an associated class.
An example:
public class IntegerAccumulator {
private int counter = 0;
@defsfn
public int increment(int valueToAdd) {
counter += valueToAdd;
return counter;
}
}
For each invokation site of the stateful function ohua creates a new instance of the associated class. And each time that particular piece of code is run the same instance of the associated class is handed to the method.
Defining stateful functions is currently supported for three languages: Java, Clojure and Scala. In theory however any language can define a stateful function, so long as it creates JVM bytecode for a class and a method.
Stateful functions in Java¶
Defining stateful functions in Java is very simple. Every stateful function needs an associated class.
The class must satisfy the following conditions
- The class must be
public
. - This enables the runtime to find it.
- The class must be
- The class has a default constructor and is not
abstract
. - Since the runtime cannot know what the constructor arguments should be it will attempt to instantiate it with none.
- The class has a default constructor and is not
- Only one stateful funciton is defined on the class.
- The class may define as many methods and members as it wants, however it may only define one stateful function. This restriction may be lifted in the future. However for the present if you wish to define multiple stateful functions in one file we suggest using static inner classes.
- If the class is an inner class it must be
static
. - Otherwise the runtime will be unable to instantiate the class.
- If the class is an inner class it must be
To define the stateful function on the class itself simply annotate the desired method with @defsfn
.
public class AssociatedClass {
@defsfn
public String concat(String a, String b) {
return a + b;
}
}
The method must satisfy the following conditions
- The method must be
public
. - Otherwise the runtime will not be able to call it.
- The method must be
- The method must not be
static
. - Using a static method will lead to an arity exception.
If you must have a static version of your code define the static method and then define a second, non static method which calls the static one and annotate this one with
defsfn
.
- The method must not be
Stateful functions defined in Java may be brought into scope for use in an algo using Bringing stateful functions into scope.
Stateful functions in Clojure¶
Stateful functions defined in Clojure may be brought into scope for use in an algo using standard Clojure require
.
Stateless Clojure functions¶
You can use any normal clojure function in ohua. User defined functions as well as library functions can be directly called in the ohua EDSL.
Warning
As of yet there is no support for lambdas
As an example, this does not work:
(ohua
(let [x (accept socket)
lam (fn [y] ( ... x))]
..)
Stateful Clojure functions¶
Stateful functions in Clojure are simply Clojure functions, which have been annotated with the metadata :init-state
.
This :init-state
metadata contains a Clojure expression which initializes the state for the stateful function.
This can be any Clojure expression and it may produce any Clojure data structure.
The exact reference returned by :init-state
will be passed to every invokation of the stateful function.
Since this state reference will be passed to the function when invoked every Clojure stateful function must have as its first argument the reference for the state, usually called this
.
Therefore if you require mutable state, we recommend using clojure atoms, mutable Java data structures or mutable clojure data structures.
There is a convenience macro called defsfn
which works like defn
but additionally takes as a second argument the :init-state
expression.
; defined with defsfn
(defsfn aggregate (new java.util.ArrayList) [this arg1]
(if (> (.size this) 6)
(let [copy (new java.util.ArrayList this)]
; mutable actions are allowed
(.clear this)
copy)
(.add this arg1)))
; defined with defn
(defn ^{:inti-state '(atom #{})} was-seen [this thing]
(if (contains? @this thing)
true
(do
(swap this conj thing)
false)))
Stateful functions in Scala¶
Defnining stateful functions in Scala is basically identical to defining stateful functions in Java.
See the requirements for the method and associated class in How to define stateful functions in Java.
Annotate the method with @defsfn
.
class Concat {
@defsfn
def concat(a:String, b:String) -> String = {
a + b
}
}
Stateful functions defined in Java may be brought into scope for use in an algo using Bringing stateful functions into scope.