hoplon

dave 2023-07-18T18:52:49.144269Z

https://clojurians.slack.com/archives/C08BDAPRA/p1687288666235129 This is coming up in 8 minutes! 🙂

1
dave 2023-07-18T20:07:11.068529Z

Here are some links to things I talked about, for anyone interested: • https://www.inf.ed.ac.uk/teaching/courses/seoc/2005_2006/resources/statecharts.pdf (1987) • https://www.w3.org/TR/scxml/ (2015) • https://xstate.js.org/docs/

2023-07-18T20:22:05.009029Z

re: rapid prototyping, i remembered a little factoid about the browser... maybe applicable to modeling/stubbing states in cljs there's exactly 1 way to prompt the user for input without dropping the JS stack, and that's window.prompt it's theoretically a way to hit an error or undefined condition and collect guidance on what to do, without using a debugger, and without dropping the stack

kennytilton 2023-07-18T20:54:27.738219Z

Sorry I missed it. There is no hope for me! 🙂 I had my head down painfully writing doc, missed the Slack ping. And I am the hugest fan of state machines' downright magical power against normally intractably hard problems, so extra bummed. I am curious about the concern mentioned of STMs not scaling as they get larger. As much as I love them, I have always used them on hard but finite problems: https://tilton.medium.com/the-foisting-of-an-infinite-state-machine-3ce986157d4c Aside: What would a state machine look like expressed as Javelin cells? I can indeed imagine a problem with STMs as a general solution in an enterprise app. The mentions I heard of multiple STMs exchanging state definitely sounds like fun 🙂 , but if that proves problematic I would start to worry. The first thing that comes to mind is "glitches". Avoiding those in Matrix (and MobX) involves the engines being able to discern reliably when state needs a refresh. What does that look like in a multi-STM environment? Fun stuff!

dave 2023-07-18T21:04:01.559639Z

I actually showed a code example that wasn't exactly Hoplon (and it was Clojure, not ClojureScript), but it was based on some actual Hoplon code that we still have running in production, from before we adopted XState. The code is so simple that it can easily translate into Hoplon or Reagent or whatever reactive cljs frontend library you use. As a quick summary: • Define an atom, cell, etc. with your initial UI state, e.g. (defc ui-state {:state :state/start}) • Define an event!* multimethod that takes two args, the current state (a map with a :state entry) and an event (a map with a :type entry) and returns an updated state • Implement the multimethod for all valid combinations of state and event • For convenience, implement a dispatch! function that just takes an event. It swaps the ui-state cell by calling event!* with the event as an argument. And that's pretty much it! From there, you can write typical Hoplon code that reacts to the ui-state cell updating.

dave 2023-07-18T21:05:39.734879Z

I hope that the XState experiment at Kevel continues to evolve so that we can try out XState's capability of having multiple state machines behaving like "actors" and communicating with each other. I'm very curious about that. It seems like a potentially elegant solution.

kennytilton 2023-07-18T22:00:24.255149Z

Thx, @dave. Great summary. But do event!* methods look at anything other than the current state in deciding the next state? That would be tough. Guessing not. But if we are talking about allowing them to work off a second state machine... well, if we look at the entire state of one state machine as a single value, it is tractable. But these STMs must represent fairly rich app states, or at least subcomponent states. One thing I found with Matrix was that my accidental choice of having all dependencies between individual properties was why it worked. Actually, I just registered "multiple state machines behaving like "actors"". OK, that is different than one STM reaching into another STM. Sounds more manageable, no need to coordinate state recalculations. But then we do not have property-to-property, we have STM-to-STM. That limited granularity could be a problem expressing certain app requirements. Everything is connected, as Buddha and physicists say. 🙂 Like I said, fun stuff!

dave 2023-07-18T22:06:13.269749Z

Does it help that events may contain arbitrary data? A message from one state machine (actor) to another could include all of the relevant information that another state machine needs in order to update its state.

kennytilton 2023-07-18T22:37:54.217569Z

"Does it help that events may contain arbitrary data?" That certainly gives us expressive power, as in "hey, I need the app to do this and this is the information involved!". But at the state flow level, what is the exact provenance of each datum this arbitrary data? The "glitch" question is, Am I using data that is obsolete based on the most recent new state entering the system? This arises when the state engine supports derived values (formulaic cells) that cascade, and the recalculation sequence of the state engine does not recalculate in the right order, so I end up using a derived value that has not yet been recalculated. A race condition, basically. MobX, re-frame, and Matrix all have solutions for that. Will multi-STM Xstate do the same? Or maybe it does not matter, because the system is architected cleanly enough that one STMs bundle is another STMs datum? That would work, but then we lose the expressivity of property-to-property across the STMs. "A message from one state machine (actor) to another could include all of the relevant information that another state machine needs in order to update its state." We still have the question: is all that state current with the most recent state input? And with such a bulk solution, we lose a key win from the property-property solution, the granularity. Matrix began when I tried a bulk dependency solution, and it turned out that different real-world cases required different properties to be calculated in different orders. As long as each property could ask for other properties specifically, there was never a clash, because the real-world problem itself was never ambiguous about the state dependency. It was only when I tried a bulk approach that the calculation could not be ordered naturally. But I quickly saw that, as long as I worked at the property level, no issues arose. It will be interesting to see how the Kevel experiment goes!

2023-07-18T22:46:46.710019Z

I was thinking about this more too, and along these lines, and I came to convince myself that javelin (and cells ™️ ?) Are already kind of "maximal" state machines, in the sense that whether or not to transition any given cell is a function of the new value + values of dependent cells. In the minimal case, these values are state name keywords and the events are transition names

kennytilton 2023-07-18T23:04:54.401369Z

I was thinking the same thing, that Javelin and other declarative formula-supporting engines implicitly define state machines, although I say "implicit Redux/re-frame like DAGs". During the bit of the call I caught, I wondered if we also capture state machines. I have not thought it thru yet, but this chat so far has me thinking. In a declarative/formulaic situation, one of the beauties is that no source datum needs to worry about who is using it. And there will be multiple users of any datum. So a state change cascades out to multiple data changing. An FSM moves from one state to one new state. Kind of a "mono" deal. But with Javelin and Matrix, we can have side effects on individual properties, because the DAGs are authored that way. But only FSMs have that amazingly expressive grid of states v inputs that make problems so manageable. Having them explicit is essential to being able to program them by eyeballing together the states and inputs. Hmmm...does Xstate support that, or is it scattered over so many methods? Uh-oh. 🙂 Maybe a nice macro can rewrite a text grid into the necessary methods? Maybe a hybrid solution is best? Javelin integrating FSMs?

2023-07-18T23:55:23.481229Z

thinking-face