Dynamic Linking¶
The main idea of dynamic linking is to have higher-order state machines, i.e. state machines that take one or more state machines as arguments, and to use the passed state machines in links, i.e. dynamically link to them.
Consider a simple example, where we want to say hello to someone. Instead of hardcoding the name, we want a state machine to provide the name. This way we could have different strategies for getting the name like returning a hardcoded value, or retrieving the name from a service.
Defining a state interface¶
First we need to define the type of the state machine that provides the name. The type of a state machine is also called state interface. It could look like this:
{
port done
resultType string
}
We need a port to know when the state machine is finished. The result is simply a string and will contain the name of the person we want to say hello to.
Creating a dynamic link¶
Now we can implement the state machine say_hello_to:
say_hello_to {
port done child("say_hello").port("done")
parameterType {
{
port done
resultType string
} get_name_state;
}
--> get_name <- parameter.get_name_state {
port done -> say_hello
}
say_hello {
port done true
parameterType string
entry @{
printf("Hello %1!", parameter)
}@
} where child("get_name").result
}
Note that the type of the parameter field get_name_state is exactly the state interface we defined above.
In the first state get_name we dynamically link to the given state machine.
As soon as the linked state finishes we execute the actual logic in child state say_hello, namely saying hello to the returned name.
Creating a state reference¶
As a next step we need some state machines that fulfill our state interface above. To keep it simple we return some hardcoded values, e.g.:
get_name_jane {
port done result ~= nil
resultType string
entry @{
setResult("Jane Doe")
}@
}
and
get_name_bob {
port done result ~= nil
resultType string
entry @{
setResult("Bob Smith")
}@
}
Using say_hello_to, i.e. linking to it, could be done like this:
say_hello_to_everyone {
port done child("say_hello_to_bob").port("done")
--> say_hello_to_jane <- say_hello_to {
port done -> say_hello_to_bob
} where {
get_name_state: {
state: "get_name_jane";
parameter: {};
};
}
say_hello_to_bob <- say_hello_to {
} where {
get_name_state: {
state: "get_name_bob";
parameter: {};
};
}
}
Executing say_hello_to_everyone with ride log running in parallel prints
$ ride log
[ INFO] 10:48:49.755 UTC: Hello Jane Doe!
[ INFO] 10:48:49.759 UTC: Hello Bob Smith!
Using an anonymous state¶
Since creating root states for every state reference can be quite cumbersome, Lingua Franka also supports anonymous states. We can rewrite say_hello_to_everyone, without separate root states:
say_hello_to_everyone {
port done child("say_hello_to_bob").port("done")
--> say_hello_to_jane <- say_hello_to {
port done -> say_hello_to_bob
} where {
get_name_state: {
port done result ~= nil
resultType string
entry @{
setResult("Jane Doe")
}@
};
}
say_hello_to_bob <- say_hello_to {
} where {
get_name_state: {
port done result ~= nil
resultType string
entry @{
setResult("Bob Smith")
}@
};
}
}
Setting state references via Lua scripts¶
It is also possible to set state references via Lua scripts. We modify the context menu of say_hello_to_app, so that it provides only a name. This name is then used in the entry script to create a corresponding state machine reference:
say_hello_to_app_2 {
port Success child("say_hello").port("done")
port Error false
clientData {
type: "app";
name: "Say Hello 2";
contextMenu: @{
<step id="name" name="Say hello to" class="flex-column">
<drop-down-menu params="
parameter: parameter('name'),
default: 'jane',
items: [ {value: 'jane', text: 'Jane Doe'},
{value: 'bob', text: 'Bob Smith'},
],
step: step
"></drop-down-menu>
</step>
}@;
}
parameterType {
string name;
}
variableType {
{
port done
resultType string
} get_name_state;
}
resultType {
string error_cause;
}
entry @{
setVariable({
get_name_state = {
state = "get_name_" .. parameter.name,
parameter = {}
}
})
}@
--> get_name <- variable.get_name_state {
port done -> say_hello
}
say_hello {
port done true
parameterType string
entry @{
printf("Hello %1!", parameter)
}@
} where child("get_name").result
} where {
get_name_state: nil;
}