-
Notifications
You must be signed in to change notification settings - Fork 1
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
Hooks #255
Comments
Remember, we lost a user over this Get this done |
I was also very excited about the jssm project - especially the DSL and graphing - until I realized there were no callback functions/eventEmitter support. Graphical view for system validation is extremely nice, but without events there is so much glue that it looses it's advantage over something like Finity with no DSL and difficult to read source but native event support. I think the hardest part is going to be incorporating into the DSL... though looking through samples, I think that the extreme test machine the An alternate approach would be to forgo DSL entirely and just populate the Machine object with functions per the state name. This has the advantage of boilerplate reduction, convention over configuration, and keeping the DSL tight. For example, the Javascript-State-Machine project just invokes functions if present, and adds accessors (this can probably be done with a proxy, or by actually adding them during parse).
presumably you'd also want If Machine inherits EventEmitter (which is pretty cheap) then the default 'onState' functions can invoke If all of this seems too expensive for cases where it is unused, then a small DSL extension like
would let users opt in... or a shorthand (RegExp-ish
Just some thoughts. On a tight deadline so I will have to use something else, but I hope to use (~> contrib?) to jssm in the future. Great work thus far!! PS - regarding data-pass path, my (totally arbitrary and entirely personal) preference would be to follow the DOM-ish model where the callback would get the source (the Machine, and any properties set on it) followed by any args passed to the trigger.
(edit: that was weird autocorrect... ) |
that's actually almost exactly the planned api (enter rather than emit because of ISO naming) the reason i've been dragging my heels is that this is going to be cross-language, and defining that in a cross language way is brutal i'm thinking about opting out tbh and mumbling some "the implementation of which is left to the reader" type nonsense |
yeah I saw your cross-language tickets... very ambitious... but at least in node (jssm) it is a very tough sell w/o any support. The EventEmitter->jssm is low priority, one line of code could bind all actions... const fsm = sm`Red -> Green -> Yellow -> Red; `;
const bus = new EventEmitter(fsm);
fsm.list_actions().forEach( a => bus.on(a, fsm.action.bind(fsm, a) ) ); but a correct jssm->EventEmitter is not possible without completely re-implementing the would you be open to at least adding a couple empty functions on guard(what: string, action: StateType, lastState: StateType, nextState: StateType, data?: mDT): boolean {
return true; // TODO implement events+guards
}
mutating(direction: string, state: StateType, data?: mDT): void {} and stubbing out action(name: StateType, newData?: mDT): boolean {
if (this.valid_action(name, newData)) {
const edge: JssmTransition<mDT> = this.current_action_edge_for(name);
if(!this.guard( 'action' , name, this._state, edge.to, newData ) ) return false; /* NEW! */
this.mutating('exit', this._state, newData);
this._state = edge.to;
this.mutating('enter', this._state, newData);
return true;
} else {
this.guard('invalid_action', name, this._state, null, newData); /* NEW just a callback */
return false;
}
}
transition(newState: StateType, newData?: mDT): boolean {
if (this.valid_transition(newState, newData)) {
if(!this.guard( 'transition' , 'transition', this._state, edge.to, newData ) ) return false; /* NEW! */
this.mutating('exit', this._state, newData); /* NEW! */
this._state = newState;
this.mutating('enter', this._state, newData); /* NEW! */
return true;
} else {
this.guard( 'invalid_transition' , 'transition', this._state, newState, newData ); /* NEW! just a callback */
return false;
}
}
// same for forced_transition stays synchronous/deterministic, no changes to the DSL, only two new tests to keep 100% coverage (again, major props there), and the pattern is portable across any language with inheritance/prototyping. In ES, the JIT will immediately prune the branches and empty functions -- I'd bet $1 there would be no affect on performance. And then, for those readers so inclined, we could have decent event support with three nominally-one-liners: const fsm = sm`Red -> Green -> Yellow -> Red; `;
const bus = new EventEmitter(bus);
fsm.list_actions().forEach( a => bus.on(a, fsm.action.bind(fsm, a) ) );
fsm.mutating=(direction, state, data)=>{ bus.emit(direction, state, data ); if(direction==='enter') bus.emit(state, data); }
fsm.guard = (what, action, lastState, nextState, data) => {
try {
bus.emit( what.startsWith('invalid_') ? what : `>${action}` , {action, data, lastState, nextState}) ;
return true;
} catch(e){return false;}
}; or something like that... anyway, just my $0.02. I'd offer a PR but I'm not a TypeScript guy and the code is too pretty for me to butcher. p.s. I ended up going with Finity not just for events but also because of the async support and builtin timeouts, which the above doesn't cover (though it is also easy to shim). Have to say after playing with jssm it is EXTREMELY annoying not being able to paste the code into a web-based viewer to verify the diagram. So quit dragging your feet already ;P but really, perfect is the enemy of good enough. p.p.s. per the checklist, this covers : ✅ Entering: edit(again): sorry, It's late, should have been |
Got asked again by another new user in email today Really need to get this done |
i just saw this 😂😂😂😂😂😂😂😂 my code is, in reality, a trash fire |
Eye of the beholder 😉
But really, the simplicity is very nice.
Looking forward to see what you come up with. No pressure hah
…On Thu, May 6, 2021 at 18:30 John Haugeland ***@***.***> wrote:
the code is too pretty for me to butcher.
i just saw this
😂😂😂😂😂😂😂😂
my code is, in reality, a trash fire
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#255 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABJ5Y4WJVWCKGHXNZLWHPKTTMMKBLANCNFSM4J454AWA>
.
|
@theshadow27 - hooks on fully named transitions and on actions have landed
|
and for the record i ended up implementing almost everything you suggested, in ways quite similar to what you suggested your recommendations were solid |
This is the core issue for hooks as a concept
Hooks tell the outside after something has happened. They offer the ability to reject transitions, modify the internal data state, consume from the input tape, push onto the output tape, throw errors, halt the machine, or cause transitions. All of these things can be initiated from outside code.
Cross reference
Hook lifecycle is defined in #487. Notation is found in
Hook notation
#622, and short notation in #619. Transactionality is in #452. Required hooks are described in #620. Fluency is discussed in #596. Hook closure is in #617. The external hooking API is described in #700; the basic hooking API is described in #660, and the fluent in #699. Posthooks are covered in #896. Hook next is described in #952.Specific hooks needed
The Basic hooks are almost done. The future hooks are mostly not.
todo: get the terminate variants and add them here
todo: hooks for data change
The text was updated successfully, but these errors were encountered: