Tiny helper function to create fluent interfaces/APIs in a somewhat consistent way.
> npm i mu-ffsm
Use it
var mkChained = require('mu-ffsm');
The idea is that you construct a fluent builder with some initial state
of type S
by calling an entry function. Then each chained call
transitions the state to a new state.
The value you get from chaining a bunch of calls can be executed as a function, which calls exit to construct a value from that state and any options you pass in.
- entry : * ⟶ S
- transition : (S ⟶ *) ⟶ S
- exit : S ⟶ (* ⟶ *)
For example
var API = mkChained({
0: function(opt) {return ;/* create initial state */},
then: function(s, opt) {return s; /* new state */},
whut: function(s, opt) {return s; /* new state */},
1: function(s, opt) {return ;/* compute final value */}
});
So 0
, 1
are entry, exit functions. All other functions transition an internal state.
All functions can take arguments, eg. opt
We create an instance of our newly crafted API,
var call = API() // entry
.whut() // transition
.then() // transition
.whut(); // transition
And call it
var result0 = call() // exit
, result1 = call() // exit
Import
var FFSM = require('mu-ffsm');
Create language/machine.
// internal state is an Array
var Talker = FFSM({
0: function() { return []; }, // TODO allow const
talk: function(say, what) { say.push(what); return say; },
1: function(say, sep) { return say.join(sep || ' '); }
});
Construct sentences/instances:
var cowboyGreeting = Talker()
.talk('howdy')
.talk('cowboy');
// make dramatic
console.log(cowboyGreeting(', ...'));
What you define
var M = FFSM({
0: function(i) { return /* initial state */; } // entry function
a: function(s, t) { return /* new state */; } // transition 'a'
b: function(s, t) { return /* new state */; } // transition 'b'
1: function(s, x) { return /* final value */; } // exit function
});
What gets called with which arguments
var i = M(entry) // x : S ← M.0(entry)
.a(trigger_0) // y : S ← M.a(x, trigger_0)
.b(trigger_1) // z : S ← M.b(y, trigger_1)
.a(trigger_2); // i : S ← M.a(z, trigger_2)
Finally
var y = i(x); // y ← M.1(i, x)
So we have
-
First
M(entry)
creates a new machine instance of type M. It's initial state derived computed as0(entry)
. -
Then
.a(trigger_0)
transitions the machine with transitiona
to a new state, using the previous state and the data fromtrigger_0
to compute the new state. -
Similarly
.b(t_1)
,.a(t_2)
. -
Finally, the
i(x)
call constructs an element out of the internal state and the argument using the exit function1(i,x)
.