mratsim / Synthesis

Synthesis is a compiletime, procedure-based, low-overhead, no-allocation, state-machine generator optimized for communicating processes and threads
Other
89 stars 5 forks source link

Applying a single event to a state machine #8

Closed dsrw closed 3 years ago

dsrw commented 3 years ago

I'm looking to use a state machine to manage my the ui for my app. It has a vimish modal interface where user events mean different things depending on both the mode and on other factors such as what is visible or has focus. It's a mess of spaghetti code currently, and I feel like a state machine could make it easier to evolve.

Could synthesis be a good fit here? In all of the examples I can find, synthesis is basically the "main loop" of whatever it happens to be managing. A state machine gets called and then runs until it reaches a terminal state, generally performing multiple transitions along the way. What I think I want is an object that I can pump events into one at a time that performs a single transition, then returns.

It seems like I could maybe achieve something like this by writing some behaviors that can jump to the correct currentState based on a mutable object that gets passed to the synthesized proc, which then performs a transition, mutates the passed object, then interrupts and terminates. When the next user event comes in I'd call the proc again, passing the same state object and the new event.

Edit: Closure iterators seem like a better solution.

Is this at all reasonable? I feel like it probably isn't, but I barely understand this stuff. Is there an obviously better approach that I'm not thinking of?

dsrw commented 3 years ago

Thinking about this a little more, could I make the synthesized proc a closure iterator?

dsrw commented 3 years ago

I created a quick proof of concept for a synthesized closure iterator. The goto pragma doesn't seem to work in a closure iterator, so I added a flag to optionally run in a while loop instead. I also had to add nnkIteratorDef to the allowed proc list. Otherwise it didn't require any changes to Synthesis.

https://github.com/dsrw/Synthesis/commit/df8ba5930c404cf5f6cecd0f6ca26fde925152ec

I'm not sure if this is a good idea, but it's pretty close to what I'm looking for.

mratsim commented 3 years ago

What you want is closure iterators or coroutines see: https://eli.thegreenplace.net/2009/08/29/co-routines-as-an-alternative-to-state-machines

And the very start of my research on continuations: https://github.com/weavers-guild/weave-io/blob/master/research/README.md#state-machine

The goto pragma doesn't interleave within closure iterators because the implementation of closure iterators also rely on it https://github.com/nim-lang/Nim/blob/v1.4.2/compiler/closureiters.nim#L914

Synthesis could be used to implement closure iterators. You only need to store a "token" somewhere that just tells the state to run on next call.

The underlying notion underneath that you are looking for is "continuation" and "resumable functions", which represent what is the rest of the computation to run.

Note that you might want to use async code to manage that UI so that all events and their handlers are registered in an event loop and asyncdispatch or chronos or any other implementation you like deal with the closure iterators/state machine.

dsrw commented 3 years ago

Thank you for the thoughtful response. I'm a fan of the declarative nature of Synthesis, but a big case inside of a closure iterator sounds like a simpler way to get what I want. I'll try that and see how far I get.