Hacker News new | ask | show | jobs
by diggan 1957 days ago
I'm fairly sure I've said this before here and elsewhere, but bears repeating, especially for this post.

Statecharts is currently probably the most undervalued tool when it comes to programming GUIs with state. Statecharts are a continuation of state machines, but with less footguns and better abstractions to be able to build larger systems.

In the end, you either build a GUI that are using state machines implicitly, or explicitly. Tends to be less prone to bugs if you do so explicitly.

If you're interested, here is some starting points (copied from an older comment of mine):

Here is the initial paper from David Harel: STATECHARTS: A VISUAL FORMALISM FOR COMPLEX SYSTEMS (1987) - https://www.inf.ed.ac.uk/teaching/courses/seoc/2005_2006/res...

Website with lots of info and resources: https://statecharts.github.io/

And finally a very well made JS library by David Khourshid that gives you lots of power leveraging statecharts: https://github.com/davidkpiano

While we're at it, here are some links to previous submissions on HN regarding statecharts with lots of useful and interesting information/experiences:

- https://news.ycombinator.com/item?id=18483704

- https://news.ycombinator.com/item?id=15835005

- https://news.ycombinator.com/item?id=21867990

- https://news.ycombinator.com/item?id=16606379

- https://news.ycombinator.com/item?id=22093176

2 comments

I really don't mean to be dismissive of your contribution, so please take this in the best light possible:

Yuck. Use continuations with anonymous fns that tell the GUI what to do next. The state is in the closure implicitly!

I wish I had an articulate counterargument, but just look at this: https://xstate.js.org/docs/#promise-example

Statecharts are a formalism for modeling stateful, reactive systems.

Beautiful is in the eye of the beholder, and never moreso when the programmer is blind to the cost of repetition and state.

Arc got it right. http://www.paulgraham.com/arcchallenge.html

Write a program that causes the url said (e.g. http://localhost:port/said) to produce a page with an input field and a submit button. When the submit button is pressed, that should produce a second page with a single link saying "click here." When that is clicked it should lead to a third page that says "you said: ..." where ... is whatever the user typed in the original input field. The third page must only show what the user actually typed. I.e. the value entered in the input field must not be passed in the url, or it would be possible to change the behavior of the final page by editing the url.

  (defop said req
    (aform [onlink "click here" (pr "you said: " (arg _ "foo"))]
      (input "foo") 
      (submit)))
In no way was that dismissive, I thank you for providing another perspective, that's always welcome in my book!

I don't necessarily agree with all the implementation details of xstate, in particular to where the logic tend to be located in practice, and the reliance on the Actor model for many things in the wild. I rather try to guide people to Statecharts as a paradigm overall, and if you happen to use JS, I think xstate is probably the most mature library there. But as all libraries/frameworks, they can be over-relied upon.

If you're in the Clojure/Script world, which is where I mainly locate myself, then https://lucywang000.github.io/clj-statecharts/ is all you need and so far the library I've had the best luck with.

I came back to edit my comment and remove the harsh words, but you left a very kind response. Sorry.

This is actually a fascinating example: https://lucywang000.github.io/clj-statecharts/docs/get-start...

... because it highlights precisely my criticism.

  ;; define the machine
  (def machine
    (fsm/machine
     {:id      :lights
      :initial :red
      :context nil
      :states
      {:green  {:on
                {:timer {:target  :yellow
                         :actions (fn [& _]
                                    (println "transitioned to :yellow!"))
                         }}}
       :yellow {:on
                {:timer :red}}
       :red    {:on
                {:timer :green}}}

      :on {:power-outage :red}
      }))

I just ... don't understand why anyone would do it this way. The code itself already says what to do. Adding this sort of data only subtracts from clarity with no additional flexibility.

You might argue that the data model makes it flexible. But I look at that and go, that's what a function is for. The only thing you need is `(println "transitioned to :yellow!")` inside of a function called transition-to-yellow, or if you're feeling adventurous, a function called transition-to which takes yellow as an argument.

From my perspective, it's better as a data structure because I can do more with it.

It's much easier for me to introspect, and I can easily build dynamic state-machines by changing a data structure, take a look at interceptors[0] as an example. There the stack is dynamically alterable based on what is within the request and each piece of middleware can look at the current context, analyse it and behave accordingly.

I write a lot of workflow based systems with dynamically changing functionality based on user input. This sort of thing is invaluable in that context.

- [0]: http://pedestal.io/guides/what-is-an-interceptor

I guess your comment highlight the real problem of trying to describe tools for handling complex scenarios with just simple examples, they often don't make sense because the first thought is always "Why not just have one function instead of all of that?".

If you're just printing some text to the console when doing one thing, then surely one function is enough. Once you start having some state and different views depending on values, normal state machines might be enough. But eventually, your application might grow so much in scope that you want to have state machines nested in other state machines, and you want to avoid the classic "state explosion" that you end up with when using state machines, then Statecharts are a very useful tool.

But before that, do the simplest thing that can work for your use case, complexity is evil and all that... Statecharts are simply a tool for handling complex and intertwined state, not for simple stoplights. Unless those stoplights are all connected to each other and also control/read other state machines, then Statecharts might be right for you.

Not sure if you took a look at the links I put before, but the https://statecharts.github.io/ website has a "Why should you use statecharts?" section with small descriptions and links to more elaborate information as well. Might give you a better view than what I can give.

Fair! I think we just have different perspectives. HN is enormously complex (it has far more complexity than most people realize or truly appreciate), yet it handles every case without any state machine: https://github.com/shawwn/arc/blob/arc3.1/news.arc

And it's nothing but a long list of functions that use closures.

I did take a look at your examples. I just ... well, we'll have to agree to disagree, and I'll try not to be so harsh in my criticisms of what I perceive to be ugly ideas. Just because I think an idea is bad, doesn't mean it's bad.

But there is one specific critique: the state machine approach will make programs longer, and consciously choosing to make programs longer seems fraught with danger. Every additional character you type is an additional character for bugs to hide in.

This moves the subjective "I don't like the style" to something concrete: What's the shortest program you can write with a state machine? My argument is that it's "significantly longer than the equivalent program without a state machine."

Given a bug-free spec to code generator, your only source of bugs can be in the state chart specification, so not bugs but rather errors.
XState creator here, what kinds of implementation details do you not agree with? Curious to know.
Thank you for this, I didn't know this existed for clojurescript =)... Much appreciated!
There is an edX course that covers state charts. It’s excellent, and Davis Harel is one of the co-instructors.

https://www.edx.org/course/programming-for-everyone-an-intro...

That's amazing, didn't know that, thanks a lot for sharing! Fitting that it starts today as well :)
No problem! And as just a note, it starts everyday since it is a reoccurring self-paced course. But if I remember correctly, the instructors still answer questions.

I really enjoyed learning about the hierarchical state machines using statecharts.

This isn’t really solving issues for most teams, how do you handle maintenance and tech debt of these state charts? And why only javascript example? You’re missing mobile
Statecharts are the solution to technical debt, not the source, as you're formalizing the possible states the user can be in, and "locking" it to that. You can even apply analysis to your codebase to figure out if you're actually covering all possible states and transitions.

If you take a look at the syllabus of the course, it's not about JavaScript, it's about the formalism and understanding the core concepts, without locking you to a particular technology. Whatever they are teaching in the course, you can apply to JavaScript/Swift as much as you can apply it to Desktop/Mobile.

Disclaimer: haven't actually taken the course, but planning to and I've read the description of it.

This isn’t answering my original question, how do you get teams to adopt this?

Until this process is made easy this will always just be a fever dream of fools in ivory towers

I'm sorry, your "original" question doesn't seem to exist in your previous comments, so hard for me to answer it...

You get people to adopt technology or paradigms by explaining the benefits and drawbacks of adopting that set of technology/paradigm and then discussing it together with your team/s. Not sure why it would be different for Statecharts compared to any other paradigm?

What process is too hard for you now exactly, to describe states or something? You're already doing this implicitly when you write UI code with state. Statecharts only changes it from being implicit to being explicit. If you're having a hard time actually naming your states, you can use tools like https://sketch.systems/ to explore what you're building with a simple DSL, then implement it properly in the programming language of your choice.

Teams and technical debt go hand in hand. I don’t mean to sound snarky but this isn’t really practical for large teams to adopt at large companies.

1. writing code adds “debt” 2. Your solution is to now add state charts too which also adds “debt”

Where are these state charts tracked? Who maintains them? When product asks engineering to change code => you now also update state charts. Added technical debt.

If this is difficult for you to understand (the problem I’m describing is very common at large companies) I’m happy to expand more on it.

Thoughts?