.. include:: alias.rst

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:

.. code-block:: lf

  {
    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`:

.. code-block:: lf
  :caption: say_hello_to.lf

  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.:

.. code-block:: lf
  :caption: get_name_jane.lf

  get_name_jane {
    port done result ~= nil
    resultType string
    entry @{
      setResult("Jane Doe")
    }@
  }

and

.. code-block:: lf
  :caption: get_name_bob.lf

  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:

.. code-block:: lf
  :caption: say_hello_to_everyone.lf

  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

.. code-block:: shell

  $ 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,
|lf| also supports anonymous states. We can rewrite `say_hello_to_everyone`,
without separate root states:

.. code-block:: lf
  :caption: say_hello_to_everyone.lf
  :emphasize-lines: 8-12,19-23

  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 context menu
+++++++++++++++++++++++++++++++++++++++++

It is also possible to set state references via the context menu. We modify the
state machine `say_hello_to` a bit to become an app and add a context menu:

.. code-block:: lf
  :caption: say_hello_to_app.lf
  :emphasize-lines: 5-20

  say_hello_to_app {
    port Success child("say_hello").port("done")
    port Error false

    clientData {
      type: "app";
      name: "Say Hello";
      contextMenu: @{
        <step id="name" name="Say hello to" class="flex-column">
          <drop-down-menu params="
            parameter: parameter('get_name_state'),
            default: {state: 'get_name_jane', parameter: {}},
            items: [ {value: {state: 'get_name_jane', parameter: {}}, text: 'Jane Doe'},
                     {value: {state: 'get_name_bob', parameter: {}}, text: 'Bob Smith'},
                   ],
            step: step
          "></drop-down-menu>
        </step>
      }@;
    }

    parameterType {
      {
        port done
        resultType string
      } get_name_state;
    }

    resultType {
      string error_cause;
    }

    --> 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
  } where {
    get_name_state: nil;
  }

Our `Say Hello` app now has a simple drop down menu, where each element
corresponds to one of our `get_name_*` state machines defined above.

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:

.. code-block:: lf
  :caption: say_hello_to_app_2.lf
  :emphasize-lines: 38-43

  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;
  }
