Hacker News new | ask | show | jobs
by diggan 1957 days ago
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.

3 comments

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!