Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

After using Mobx for a few weeks, I've come to question why anybody would use anything else. It is the only software I've ever used that was created for a dynamic language that has me trying to re-implement in my favored strong/static typed languages. Now I just point and laugh at anybody trying to make sense of their Redux global state atom with a bajillion reducers.

So far, I've only found one common objection to it: that it embraces mutable state. Well duh...UIs are state machines. They need mutable state because their entire existence depends on it. Sure, you might be able to model the mutable state of UIs using immutable data structures, but you can't get rid of the mutable state. And because of this, my opinion of this objection is that it is of a religious nature. You can ignore this particular objection with no risk at all.



I'm unconvinced by this argument.

State machines don't let you change things whenever you feel like it. There are a set number of states and a bunch of predefined transitions between them.

That structure is exactly what redux forces you to implement - whereas in MobX it's up to you to apply the same constraints.

Redux with plain JS objects a a little messy IMO, because there aren't lots of nice & readable ways to transform objects without mutating the previous version. I use it with Immutable JS and am very happy with this approach.


I made a poc with Redux and I felt that a) the boilerplate obscured my code b) the async story was pretty confusing and c) managing my derived / calculated properties was really hard.

One thing I've discovered is that a lot of the logic is derived data in the form of pure functions. A huge amount of my codebase seems to be about managing the derived state that trigger from a core bit of state changing. Really there's actually a much smaller core set of data, and then various aggregations on top of that (with the component view tree being the final representation).

I initially played with MobX a while back and had the worry that you could end up with state changes triggering off all over the place, but I don't think it needs to be like that.

There's no reason you can't have core state, layers of calculated properties and a view. Having said that, it's early days, which is why I'm looking to hear real stories of where MobX hasn't worked.

My approach when evaluating technology is to try to find ways in which it works poorly so that if I do pick it up I'll have an idea of the limitations (and can try to work around them).

MobX does also have "strict mode" where you have to declare the transition points. I know that's not really the same as the reducer model, but it does force people to think about when state will be updated.


https://github.com/redux-observable/redux-observable addresses your point b) and your point c) somewhat.


> That structure is exactly what redux forces you to implement - whereas in MobX it's up to you to apply the same constraints.

Redux doesn't force you to apply constraints any more than Mobx does. In fact, it's even less structured than Mobx...any component anywhere can emit any event that modifies any state. With mobx, you can only modify state that you have an actual reference to.

> Redux with plain JS objects a a little messy IMO, because there aren't lots of nice & readable ways to transform objects without mutating the previous version. I use it with Immutable JS and am very happy with this approach.

I agree that semantically immutable data structures are easier to comprehend and work with. But you still have to mutate state, or you don't have a UI. With immutable structures, you copy->modify->replace...with mutable structures, you mutate in place. You can't avoid mutating state with UI programming, so embracing a model that embraces intelligent manipulation of mutable state isn't an indictment, it's a tangible benefit.


The right way to get rid of mutable state is to replace it with a stream abstraction, which declaratively models how the state changes in response to events. That would be cyclejs: http://cycle.js.org/

Too bad that this model doesn't work well with React. React models the UI as a (mostly pure) function from props/state input to DOM output, however in reality the UI is a stream containing both DOM states and input events. The minimal modification would be to select the input events that a component would like to expose in its event stream, in a similar manner to Elm: https://guide.elm-lang.org/reuse/checkboxes.html


Why do you say that's the right way? Is there something intrinsically better about having the state modelled as a stream of changes? Is that really getting rid of mutable state? [EventA, EventB] is a mutation of [EventA], sure it's a WAL style, but the combined final state is a mutated version of the original state.

Presumably the concrete state model (like MobX) is a realisation of the event stream at a moment in time. As far as the user is concerned, the only moment in time that really matters is now.


Mutable state is fundamentally harder to reason about. You get aliasing bugs, stale cached values, etc.

With immutable data in an append-only stream this is simply impossible.


I've done plenty of work with immutable data, and I tend to code in a way that minimizes the amount of state I have to manage.

I guess that's what I was saying about mobx though - I can have a much smaller set of core data and then pure functions for the layers of derived data. And it's a) easy to reason about and b) super performant at managing the calculations.

Aliasing bugs don't really apply in JS, unless there's some variation of the term? Stale caching is also not an issue with calculated derived data.


Saying about "aliasing bug" I meant something like this:

    var my_data = {"foo": null}

    function innocuous(input) {
      var data = {"one": input, "two": something()};
      data.input.foo = data.input.foo || "foo"  // set a default value
      // do something with data
    }

    var result = innocuous(my_data)

    // Oops, now my_data.foo has changed.


I'm saying its the right way to get rid of it, not that its right to get rid of it :)

MobX does really, really well in minimising the complexity of mutable state. Its the best there is out there.

Where MobX does worse is when there is more complexity then a simple direct map function to calculate derivations: debouncing key input, doing async requests, creating animated transitions, etc. Its a great model, but it needs some more work to be truly amazing.

The user does care about the past. For example if the state of the input box changed too many times during the last second, they don't want every change to cause a slowdown in response time due to too many requests being sent to the server, and they want only results from the last request they sent, not the last one that was processed.

I think the reverse criticism applies for cyclejs: while it can do everything thanks to RxJS, it needs a better component composition model. The current one is too tedious and alien for my taste. (For example, why isn't the same DOM selector syntax used for component-event selection?)


You should check out cycle-react [1], as it combines the best of Cycle and React.

[1] https://github.com/pH200/cycle-react


> They need mutable state because their entire existence depends on it. Sure, you might be able to model the mutable state of UIs using immutable data structures, but you can't get rid of the mutable state.

Can you give an example of this?

I have worked on fairly complex Redux applications but have never ever mutated states and it has been just fine. In fact, I use ImmutableJS to keep my apps' states.

State machines without immutability in a large application sounds like a joke to me, sorry.


If you haven't mutated states, you don't have an interactive UI. With immutable data structures holding your state, you mutate it by re-assigning variables. This is exactly what Redux does.

In fact, the rendering benefits of immutable data structures comes from the fact that you can perform equality comparisons by reference safely, and ref comparisons are faster. Any time you re-assign a variable, you change that variable's reference, and it is no longer necessary to deep check the reference's value to know that it changed.


> If you haven't mutated states, you don't have an interactive UI.

React allows you to treat DOM mutation as an expression of current app state.

This doesn't mean that your state itself has to mutate. It's entirely possible to structure a React (and/or Redux) app with fully immutable state, copies of which represent change. (Whether copies are done efficiently, e.g. via persistent data structure algorithms, or naively, e.g. via a deep clone, is secondary).

I'm befuddled where you're getting the impression that immutable data structures themselves require mutation.


> React allows you to treat DOM mutation as an expression of current app state.

Current app state. As in not past state. Not future state. There would be no need for the distinction if the state wasn't mutated.

> This doesn't mean that your state itself has to mutate. It's entirely possible to structure a React (and/or Redux) app with fully immutable state, copies of which represent change. (Whether copies are done efficiently, e.g. via persistent data structure algorithms, or naively, e.g. via a deep clone, is secondary).

You are confusing the mutability of a data structure with the mutability of state. You can model mutable state using immutable data structures, but it's mutable state regardless. In fact, that is exactly what the Redux/ImmutableJS model is. If the state of your "immutable" react app were really immutable, you would have a webpage, not an app.


The DOM can be mutated just fine behind the scenes by React to reflect whatever brand new state has been passed into it. That brand new state can be anything. It can be a single (by definition immutable) integer. It can be a deep clone of an object. This is the ideal intended Redux architecture. Redux reducer(s) receive the state and return either the state untouched, or a non-mutated copy of the state. React reacts accordingly by expressing this brand new state as (possible) DOM mutations.


That is mutable state. Your Redux store is a mutable state container. If your redux store returns a {a:1,b:2}, and then you dispatch an action and it now returns an {a:2,b:1}, then you have mutated your state. It doesn't matter at all if the store's data structures are mutated in place or replaced with a modified immutable updated copy of a previous state, you still have mutable state.


I think I see where you're coming from, but I also think you might be moving the target a bit. I suspect you'd garner less debate if you said, I dunno, just "a dynamic UI app needs a way to model state changes". "Mutate" has a specific meaning, in my mind at least, when discussing variables and data structures. React enables avoidance of direct DOM manipulation, Redux obviates direct state mutation.


Also, "finite state machine" has a very specific meaning, and that isn't just any machine that has a finite bunch of variables that you call state.

If React lets you execute arbitrary JavaScript code (which it does), then you're no longer using just a finite state machine, you're using a Turing machine, which is something different, and more powerful. It may have many finite state machines embedded in it, but to refer to a user interface as a finite state machine is like referring to a car as a pulley, just because the engine requires several pulleys to work, and ignoring all the rest of the engine and car. You can't make a car out of just pulleys, and you can't make a user interface out of just finite state machines.


React, as it is presented to the programmer and for all practical reasoning, is programming with finite state machines. Sure, javascript is Turing complete and capable of much more (behind the scenes in React there is plenty going on that does not qualify as FSMs), and within a react application or even a component you might use Turing complete semantics to describe actions, data structures, effects, or whatever...but constrained to the API and idioms of react it is perfectly acceptable to call it FSM programming. In fact, Facebook already says this about react, that UIs are state machines and react is a way to declaratively describe them that way.

A better analogy for the equivalence of Finite State Machines and User Interfaces is the equivalence of human arms and levers. Just because it is one doesn't mean it isn't the other. One is abstract and the other is concrete, but there is still an equivalence. And not just computer user interfaces either...you could take pretty much any real world user interface: analog, mechanical, digital, whatever. Hell, tubas and vending machines are finite state machines. They fit the definition of a finite state machine because they are.


I'm really interested in what you're saying about UIs as state machines. Do you have any articles/references to back this up? Genuine curiousity here.


If you have an interactive UI, you have a state. A button is either pressed or it is not pressed, active or disabled or hidden. A text form has a text value. Your layout has state (position, color, etc.).

If your UI is interactive, you have events and actions that mutate that state. A keyboard event modifies the state of your text form field. A button press signals an action. Etc.

A state machine is `State0 + Event -> Action + State1`. Since your UI's entire functionality can be described by the state it is in combined by the Events and resulting Actions and changes in States, your UI is a state machine. This describes the superset of all UIs. Non-interactive UIs can still be described by this, although the benefits are slim because the state isn't really mutated without interactivity, so your State0 is always equal to your State1.

Note that I'm saying UIs are state machines...not like state machines. They literally are state machines. Whether you model them as such is up to you, but my claim here is that modeling your UIs as state machines has tangible benefits because they are state machines.

Funnily enough, there are plenty of articles out there that claim to make stateless UIs. These claims are false...any critical look at the model shows that the state exists, but either is modeled implicitly (keeping state in the DOM and outside of javascript, or within a rendered object, or within a closure attached to an observer somewhere, etc.), or explicitly moved somewhere else. There might be some merits to the latter model (as well as some drawbacks), but the former model is just delusion...a perfectly leaky abstraction where you have state, but have lost your ability to work with it.


The person you're replying to didn't specify whether UIs are represented as state machines which are finite or infinite, but I'll assume finite since infinite automata are more powerful than Turing machines so they presumably can represent something which is made by a computer.

UIs in general can not be represented by finite state machines. An example is a page with a button which when pressed adds something to the page. Since there are an infinite number of things that could potentially be added, this can't be represented by a finite number of states.

On the other hand, I think many UIs could be represented by finite state machines, although perhaps sometimes only roughly. You can start by trying to specify that each instance of a specific page a user can see is presented by a state, and any interaction with the user that leads to a different rendering of the same or a new page is a transition. However, this fails to account for things where a user can do something which doesn't change the current page but might change a later page. You can account for this by having multiple states which show the same exact UI but have different transitions to future pages. Of course, it isn't just the user who can interact with your system to produce a different UI for other people, so you'd have to model those transitions and states too..

I'm not a front end developer so I don't know how many UIs can actually be modeled this way, but I can see how it might be a useful way to think about things, even if it isn't necessarily always correct.


I didn't specify finite or infinite, and you're right that FSMs alone aren't sufficient to describe all UIs. But they are sufficient to describe nearly all interactive UIs.

Your infinite button example still has a finite state, but it's not modeled explicitly, and would probably be a good idea, for very practical reasons, to do so. If it really goes on forever, you'll eventually run out of memory, whether that memory is managed by the rendering engine (appending dom elements?) or by the language runtime.

> However, this fails to account for things where a user can do something which doesn't change the current page but might change a later page.

A Hierarchical State Machine model covers this scenario quite nicely...which is exactly what React is.


Interesting. I hadn't heard of hierarchical state machines. Thanks for the reply.

If you don't consider the infinite button example, then what other kind of UI can not be described by a FSM?


I've certainly never found a common UI pattern that isn't describable with FSMs, but you could probably think of a few toy examples of non-terminating state. Anything involving recursion, exponential, or asymptotic behavior for example could theoretically involve infinite states. Again, likely of no practical use.


Read any real-world user interface specification like Apple's classic Macintosh Human Interface Guidelines [1]. It specifies all kinds of complex behaviors, like how pull-down submenus don't pop up immediately as you move the cursor over them, and how dragging and dropping text handles the white space at the beginning and end of the selection, or how adjusting the end of the selection in mixed right-to-left and left-to-right text works, which is much too complex and nuanced and ad-hoc hacky to describe with finite state machines, and require a Turing complete machine to implement.

Even if it were technically possible to restrict yourself to a FSM, you would end up going insane and producing exponentially explosive unmaintainable finite state spaghetti machines.

There's a huge uncanny gap between what you can elegantly describe with a clean mathematical abstraction like finite state machines, and what real human beings expect and perceive to be easy to use in a real world user interface.

The polish of a good user interface is the result of millions of tiny little scratches, and in practice that requires the power and flexibility of a Turing machine (and not one stuck in the Turing tarpit like Brainfuck) to make all of those tiny little scratches and cover all the edge cases and special circumstances, because finite state machines are just too clumsy and not powerful enough to conveniently cover that much ad-hoc messy fractal detail.

[1] http://interface.free.fr/Archives/Apple_HIGuidelines.pdf


I'm not sure an "infinite state machine" [1] is actually a "thing" the same way a "finite state machine" [2] is a precisely defined mathematical concept in Automata Theory [3]: one level of the classes of automata more powerful than combinational logic and less powerful than pushdown automata and Turing machines.

In my mind, there is no question that finite state machines alone are not powerful enough to define practical real world user interfaces, which require the power of a Turing machine. The fact that most user interfaces incorporate many finite state machines at different levels doesn't mean that finite state machines alone are sufficient.

That's why we program browsers with JavaScript, not FSMML or FSMON.

[1] http://c2.com/cgi/wiki?InfiniteStateMachine

[2] https://en.wikipedia.org/wiki/Finite-state_machine

[3] https://en.wikipedia.org/wiki/Automata_theory


I second that. My experience shows it is a state machine, but I have not see any formal study of it.


Well everything that happens in a real computer is an FSM. There's a finite amount of memory and therefore a finite number of states.


That's playing fast and loose with the literal every-day definition of the words "finite", "state" and "machine".

There is actually something in computer science with a precise mathematical definition called a "finite state machine," [1] that is not simply any machine that has a finite number of states.

When you use that term, you're not just talking about what it can do, but also what it can't do, because "finite state machine" implies that it's just a finite state machine, not a pushdown automata, and not a Turing machine.

With a Turing machine, you can implement a finite state machine or a pushdown automata, but Turing machines and pushdown automata are more than just finite state machines.

That said, user interfaces typically use lots of finite state machines at different levels (hierarchical, and unrelated), but they also require more powerful machines like pushdown automata and Turing machines (for validating email addresses, for examples).

If all you had to program user interfaces were finite state machines, but no Turing machines, you would be a very frustrated programmer, and your users would be unhappy to have such a brittle inflexible user interface.

[1] https://en.wikipedia.org/wiki/Finite-state_machine


It's not playing fast and loose at all - at least not with the precise mathematical definition (perhaps it is playing fast and loose with the "literal every-day" definition which might mean a state machine with few enough states to fit on a whiteboard). A push down automata is defined as having infinite stack and a Turing machine is defined as having infinite tape. A finite stack push down automata permits an equivalent (much larger) finite state machine. A finite tape Turing machine also permits an equivalent (much much larger) finite state machine.

Leslie Lamport makes the same observation on page 4 here: http://research.microsoft.com/en-us/um/people/lamport/pubs/s...

The comment I replied to was "My experience shows it is a state machine, but I have not see any formal study of it." - I was simply replying "of course it's a state machine".

Lots of situations in GUIs can be modelled as quite compact state machines - but it depends on the level you model it at. If you need to model the state of a text box then your state machine will grow very large since you need a new state for every possible input string. Given modelling simplifications where we abstract away some parts with large state spaces by collapsing states, there are very few pieces of software which don't benefit from being modelled with state machines.

Personally I would be a very frustrated programmer and if I had to program user interfaces with Turing machines since writing a transition table would feel awfully low level and since the Turing machine model doesn't have any facilities for user interaction I'm not sure any users would be terribly happy with the result (so I'm not quite sure what you're trying to say here).




Consider applying for YC's Winter 2027 batch! Applications are open till November 2.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: