It works and I do use it from time to time. In many cases its overkill to do this, and doesn’t really seem like it reduces errors. After experimenting with it for a while, I more frequently just use a type to enumerate the states and implement the state transitions in the update function.
I have found a use for it sometimes where I designed a state machine to describe how a UI should behave before writing any code. I tend to do this as a series of pen and paper sketches; quick iterations. It can then be nice to code that up as a pure state machine, before tackling the bigger job of writing the view.
This is something I really wanted to do at first, and some of the examples here are far more successful than my kludge ever was.
The thing is that Elm is all about defining the transitions between states. That is literally what you are doing in the “update” function, and the code you do or do not write is what makes transitions possible or impossible.
So in my opinion, the best way to “make impossible transitions impossible” is to create a library for your type that only exposes the desired transition functions. I think if Elm could prevent you from writing certain transitions within that library, it would literally be able to write code for you.
So this is one way of doing that using phantom types:
When I say its overkill, I suppose I mean that I am both the author of that library and the consumer of it in my update function. As I only use the library for that one update function, and as I already know what the state transitions are restricted too, splitting that into a separate abstraction feels a bit like making work for myself.
I suppose it might work differently in a team setting, if say I was a team lead and I used this technique to sketch out the state machine for some part of the UI quickly, and then handed it off to another programmer to write the view.
I have used this technique, since that tends to be how I go about making UIs; paper sketch for quick iteration, design the state machine in code, write the view and update function around the state machine. Each stage takes progressively longer to do, building on the outcome of the previous stage.