[DRAFT] Integration a FSM in CastleCode (experimenting)

As demanded by Castle has generic FSM synt... (U_FSM_Syntax), we need a syntax to define FSMs directly in CastleCode.

The behavior of a FSM is defined by the its “rules”. But to use a FSM we need to “call” that FSM; it needs to proces events, either by single stepping or in a for-ever-loop. This is typically a details that most developers like to controll.
And so we need a bit of syntax (and semantics) for it.

A few experiments are already given in Castle FSM-rule syntax tries – which focus on the rules; and are mostly “internal” to the FSM. In this blog we focus and experiments on how to integrate the FSM with the “rest of the CastleCode”.
Unlike the ‘states’ – which are internal details of the FSM–, the events and actions are external – its are the inputs and outputs of the FSM. So, the need to exist both ‘in’ and ‘outside’ the FSM.

Intro

This blog shows a few possible implementations of a simle, well-known Turnstile FSM, to experiment with syntax options. We use a basic version, and we will extend it – to show wat it does with the code.

Questions

The experiments – how to intergrade an FSM into CCastle focus around the following questions.

  1. a component (by itself)

  2. a function (or other callable)

  3. just a “statement”

Turnstile demo

We use a very simple – 2 states, 2 events, 4 actions– FSM called “Turnstile”. It has only 4 “rules”, and is easy to understand.
Later, we will add more stated (requesting 2 coins) and/or non-FSM behavior, like counting the (total number of) coins

We use a very simple Turnstile FSM as demo. It as two inputs, two states and a few actions; it’s based on #UncleBob’s State Machine Compiler demo.

Experimenting

Caution

Most of the shown CastleCode syntax is provisional.

  • Here, we use basic rule synax of Try 1,

  • I use @FSM (see: “Rewriters (TODO)”) as a kind of (Python) (decorator) or CPP2 [1] metafunction (video-link)

  • I use some “ad hoc syntax, for trivial thinks, I did’t design yet (e.g. Enum).

  • The order of names and typs are not consistent name ‘:’ type [‘:=’ value] vs type ‘:’ name – sorry for that; my tought are not final, and my fingers type differently:-)

FSM as Component

When an component is implemented as an FSM (using the “Rewriters (TODO)”: @FSM) the in-ports are used as FSM-inputs and the out-port can be used for actions. So, “calling” the FSM is like “calling” any other component: send signals over the connections.
Note: There is no need to that alls FSM-inputs (nor it outputs) match 1:1 to the protocol of one port – the can originate from multiple in-ports and protocols.

protocol Turnstile_inputs {
   coin;
   pass;
}

protocol Turnstile_actions {
   unlock();
   thanks();
}

protocol Gen_errors {
   alarm();
   ...
}

As you can see in the code above, only in the (component) implement it is shown it is (implemented as/with) an FSM. This a future implementation may be differently, without any change to it users.

Eval (Component)

I like this variant!

The Turnstile FSM shows that a basic FSM in this style is very clean, and simple. Later, we will it’s expandable to; see: Counting Turnstile.

FSM as function

As a FSM has state it can’t be a “pure function” – like a math-function: always returning the same answers on the same input, without side-effects. Pure function have great benefits for concurent computing. However, most programming “functions” are (typical) not pure, and “procedures” would be better name, most language uses the term function.
In line with that, we use the term ‘function’ too; in a general way.

In that view, having state is not an issue. Programming-functions can have “state”, by example by having a static (local) variable, as in e.g. C/C++.
The state of a FSM-as-functions is like a static. And as we can see below, it results in quite easy syntax.

@FSM  // Experiment: a FSM as function
Turnstile(signals:Turnstile_inputs, act:Turnstile_actions, err:Gen_errors) {
   state: Locked, Unlocked := Locked;

   // State + event -> State, action();
   Locked 	+ signal.coin	->  Unlocked,	act.unlock();
   Locked 	+ signal.pass	->  	,	err.alarm();
   Unlocked	+ signal.coin 	->  	,	act.thank_you();
   Unlocked	+ signal.pass	->  Locked,	act.lock();
} // The syntax is clear -- BUT what are the semantics? (how to call)

There are some complication with this concept, however! When rewriting this FSM-function to a regular-function with a local static variable, there is only one (memory) place to store the state, even when that FSM is used at multiple places.
So, we need a kind of variable-instance pro use of the same function – that is what we typically call a class (see: FSM_class, below).

Another tough questions is how to use “call” it, and what does it return?
Conceptually, a FSM does not “return” anything, so it would be a void function. We could return the action to be called – which is a bit strange, but making the FSM call them is strange too.

Simular, when calling: what parameter(s) to pass?
In the example above, we pass the list of signals (as a Protocol), not the “current signal”. That “list” is needed as the rules use it – we can’t compile (aka: verify) the transactions without the list. Again, this can solved in a class-alike setting, e.g. by introducing a “step” method; which passes the current signal.

Eval (typical function)

As shown above, using a FSM as a (classical) function is problematic. The syntax is easy to read & write, but the semantics are complicated. And even when it is possible, it not easy to use. So, this approach is turned down.

Function syntax, to build a (local) component.

Another approach to use the function-syntax is to consider a FSM as a (kind of) iterator. Then the FSM-function becomes a generator, as by example in Python. And the function –roughly as shown above– returns an object that is the FSM.
This solves most (all?) of the described complications.

Returning a class-object

In most languages an iterator is (like a) class-instance. So, it can store the state. When Calling the FSM-function twice, two independent FSMs “instance” –each with it own state– are returned.
Given the (returned) FSM is an instance, “calling” it becomes simple. We can have several (build-in) methods, like fsm.step(<current signal>).

Admitting it solves the “calling” and some practical problems, other complications still arise. Like how to call the actions – are the called by the FSM, or by the code that called the FSM? Both are possible, but not perfect.
Another disadvantage is introducing “classes” (aslo see below). Classe are fine in most languages, but Castle is build around “components” – more active, And with clean interface at the “backside”.

And luckily, the is a better solution …

Returning a component-element

When the FSM-function returns a component-element (instead of a class-instance) all uncertainties are solved!

After calling that FSM-function, we have a normal component (element) with input- and output-ports (both passed as parameter). Those port can be connected to other ports, or an input can be triggered like any component. And as the element is active, the actions of the FSM result in triggering other components/ports.

The semantics of a FSM-function are exactly the same as for the FSM-component. Actually, the rewriter for a FSM-function will transform it to a “normal” FSM-component (and instantiate it).
Mark that a FSM-component is also rewritten. So the FSM-function is rewritten twice. Both by the same @FSM rewriter. That however, is an implementation detail. It may be clear that there is no loop – above we already shown that rewriting a FSM-component result is “base code”.

The only questions is: why? (and which one)
As the FSM-function and the FSM-component result in the same element, which one should we select? Or should we allow both?

In many modern languages it is possible to define code in two (or more) ways. For example in python we can define functions with the ‘def’ keyword (the usual way), or as a nameless “lambda”. The same aplies to “classes”, and is valid in many languages.
Typically, there is a “standard way”, for typical use. And a “shortcut” for small, only uses once case.

The same can be useful for Castle, as we can see in a Counting Turnstile example.

Eval (component-building function)

It is possible use the function-syntax, when it results in a component-element. Although the syntax differs from the FSM-component (syntax), it has the same semantics.

It have a certain beauty, but I’m not (yet) convinced that it has advantages to allow both syntaxes. And as the component one is closer to components (sic), the function variant is an option for now. Not more, not less.
When I add it, there is no need to implement it in the “bootstrap” compilers.

FSM as (Data)Class

Error

Class? Of Data-Classes? Or Struct’s

Until now, “classes” are hardly used in CCastle – they are kind-of replace by Components. Components however are always “active”, and we may/probably need passive “data-clases” too – e.g the AIGR has only data-clases.

Hint

As an FSM has state and behavior, a class sounds better then a Data-Class/Structure. But is it

Hint

As an FSM has state, it needs “memory”, and is typically implemented as a instantiated class in many language.

Caution

Some design-patterns use classes (with no data; and hardly an instance) for “hold” the callables for each state.

That is more namespace then a class. And surely not a data-class

Eval (Data/Class)

ToDo

FSM is not a statement

In the initial list of questions, I added the option “FSM as statement”. But in the analyse (above) it become clear that that doesn’t is a valid option. Neither the syntax, nor the semantics are clear nor have any advantages.


footnotes


Counting Turnstile

Using the Turnstile as a sub

from Turnstile_Component import Turnstile

component CountingTurnstile: Turnstile {
  port Getter<coins> admin;
}