-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Observable finite state machine #368
Comments
@mjstahl Thanks! I've got a lot of random thoughts on this. Throat clearing on how to do FSM-ish stuff in define-map right nowThe ATM example is a close approximation to the technique I normally use. CanJS usually "flips" the state machine to focus on the "extended state variables". So instead of focusing on the transitions, we derive the "state" from the variables. It looks like this: DefineMap.extend("H20",{
get state(){
if(this.temp < 32 } return "freeze";
if(this.temp < 212 } return "default";
return "steam"
},
temp: {type: "number", default: 60}, // <-- extended state variable
freeze(){ this.temp = 32 }
}) Imo, this approach works well for when there is a "source of truth" (in this case I think a FSM would be great for CanJS. (I just want to give people some tools in the meantime). Other thougths
|
The ATM example is how I do this sort of thing on client work and prior to writing my little library. To answer your other thoughts:
|
To make something like https://github.com/canjs/can-observation/blob/master/test/simple.js#L148 And here: https://github.com/canjs/can-simple-map/blob/master/can-simple-map.js#L144 The critical symbols are:
Then if someone reads get state(){
ObservationRecorder.add(this, "state");
return this._state;
} And when state changes, you'll want to make sure you dispatch all the right events:
Much of this (and more) can be mixed in via https://canjs.com/doc/can-event-queue/map/map.html That might look like: CanStated extends Stated {
has(action, updateValue) {
const transitionTo = this.states[this.state][action];
if (!transitionTo) {
throw `'${action}' does not exist as an action of '${this.state}'`;
}
if (typeof transitionTo !== 'string') {
throw `'${transitionTo}' is not a valid state. It must be a string.`
}
if (!this.states[transitionTo]) {
throw `'${transitionTo}' does not exist`;
}
var oldState = this._state;
this._state = transitionTo;
this[canSymbol.for("can.dispatch")]("state", [this._state, oldState]);
if (updateValue) this.value = updateValue;
return this;
}
get state(){
ObservationRecorder.add(this, "state");
}
}
mixinValueBindings(CanStated.prototype); |
State machines are usually quite trivial to implement. If you need this functionality directly on a |
While state machines are usually trivial to implement, I like the idea of having a consistent way to define them, especially when the simple definition creates a more complex object that is easy to use. Along those lines, I would be in favor of the edges ( Similarly, I would be in favor of a way to specify what state transitions are valid. Using the main example above, you cannot go from ice to steam (at least at STP), and being able to prevent this and present a useful error message would be nice. |
@christopherjbaker I should have been more pedantic in my example. In the example, you can transition from ice to steam because I specified it that way not thinking about the real world. To stop that from happening you would simple remove the "action". ice: {
warm: 'initial',
values: {
temp: '32F'
}
}, Now if you were in the |
Problem:
I found ViewModels with many properties to be error prone when switching between logical states in the ViewModel. Forgetting which properties to update when the state was updated was problematic.
Example API:
The text was updated successfully, but these errors were encountered: