Is TEA a comonad?

This feels like a little bit of a cheat in the simplification and/or line count since Rupert did have functions that modified shown. He just didn’t use update to access those functions. Once one starts arguing against TEA “triplets”, it feels like a bit of a stretch to use the fact that an update function doesn’t modify something as a reason to remove it. On the other hand, this does point to a clear way that Rupert’s code can be refactored and simplified because it factors into a portion that deals with animated showing and hiding and a part that deals with buttons. The animated show and hide may well justify being in its own module for reuse elsewhere and can be separated from a buttons implementation much more like Richard’s. So, a full refactor should probably break the code into a buttons module (view function) and a show/hide animation model and leave it to the containing code to stitch the two simpler pieces of code together.

All of these could arguably be further simplified by replacing the use of string names for buttons with a message parameter on the button type. Then the configuration step can just supply buttons with icons and messages.

A bigger stylistic question is whether one adds an animation parameter to view functions when the design calls for animation or as a matter of practice. Type-checking makes it easy to add later, but in my experience engineers often become reticent to make changes in code they didn’t write and also to make changes that will require updating all uses. The latter can be mitigated by adding a viewWithAnimation function when we find a need for an animation but the first friction point remains. The correct call probably depends on team dynamics and cross-project code reuse. If people can feel comfortable updating an API both because they are used to it and because the consequences can be limited to the project at hand, then keep things simple while establishing a clear pattern for the more complex case. If you can’t make those guarantees, then it may be better to build for the general case from the beginning.

Turning back to the start of this thread, this trade off goes to some of the questions around any sort of nested use of TEA. When TEA really was triples, it didn’t feel that bad. A bit of boilerplate but it was manageable. But then 0.17 brought subscriptions which are often null but result in code that doesn’t work if you don’t hook them up when they are needed—possibly for non-obvious reasons. So, now we’re collecting and mapping commands in init, collecting and mapping subscriptions, processing nested messages in update, and mapping messages in view. None of it is hard, but programmers balk at boilerplate. And if you need to communicate between parent and child, the boilerplate gets messier still. The type system can be used to make this code reliable, but it still leads to a lot of code. At this point the pendulum swings toward avoiding fractal decomposition and pushing more things into the top-level model because there’s a reason people have been drawn for years to just sticking stuff in global variables which is essentially what tossing everything into one big model is essentially doing.

As someone who reaches for C rather than C++, Elm’s relative lack of sophisticated mechanisms that would reduce boiler plate hasn’t bothered me as much as it does some. Boiler plate feels better than magical pieces of indirection that by not being invisible and dead simple aren’t really all that magical.

Finally, and I think this ties to the login token case, where a components view can definitely lead code astray is in structuring the data model based on the view hierarchy even when that doesn’t make sense. Going flat “fixes” that by removing hierarchy as an issue. Redux fixes that in JavaScript by have one data model store. One could apply the Redux pattern to Elm using something like the Elm taco pattern or if flat by encapsulating the entire data model in a single entry in the model. Wrapping the data model in an effects manager would allow changes to the data model to be handled via commands from the UX just as commands to a server would be, but this has its own challenges since effects managers can’t use effects from other effects managers.

Mark

2 Likes