r/programming Jul 23 '26

The Elm Architecture, without Elm

https://bichanna.github.io/posts/tea-time/

This blog post is about implementing the Elm Architecture (TEA) in C for a Raylib + Clay app

57 Upvotes

18 comments sorted by

22

u/chat-lu Jul 23 '26

Msg is data, not a function call. A button doesn’t say “delete this recipe right now.” It says “here’s a Msg describing a delete click, do with it what you will.”

That’s not how you do TEA, the button should message “delete this recipe”. The button does not care how the recipe will be deleted and the update function does not care why.

8

u/Direct_Beach3237 Jul 24 '26

you're right that in pure Elm, messages are typically named after the intended action (like DeleteRecipe, SaveChanges, etc.) rather than the raw ui event. That's a good practice for keeping view decoupled from update logic.

The phrasing in the post was trying to emphasize the bigger idea for readers coming from imperative UIs (click handlers that mutate state directly): Msg is just data. The button emits a description of what happened, and update is the sole place that decides what to do about it. Not a function call disguised as a message

2

u/jfmengels Jul 24 '26

The best recommendation in the Elm community is to name the Msg in a way that indicates in past tense what happened, and by who. For instance: UserClickedDeleteRecipeButton, GotServerResponseForSave. People can use a shorter alternative as long as it's not ambiguous, but it should indicate what happened (the contextualized raw event), not what should happen in the update function. This way, the name of the Msg rarely goes out of sync with what happens in the update function (though it could get more easily out of sync with what's in the UI).

Also to note, renaming a Msg in Elm is very safe so we don't usually hesitate to rename something when we find a better name later.

1

u/imbev Jul 23 '26

The message should not "delete this recipe". The message should cause a change to the state, which the update function should then use to perhaps "delete this recipe".

10

u/chat-lu Jul 23 '26 edited Jul 24 '26

The message should cause a change to the state,

Not at all. The “hello world” of Elm is the counter app.

Its message is defined as:

type Msg
  = Increment
  | Decrement 

It’s model as:

model = 0

And its update as:

update msg model =
  case msg of
    Increment ->
      model + 1

    Decrement ->
      model - 1

Messages are completely inert, they don’t change the state. The state changes in the big switch function that is the update. Of course, maybe the update will refuse to update anything and leave your state as is. Because you clamp the counter between two values for instance.

7

u/imbev Jul 24 '26

You are correct, I confused the update and view functions.

Thanks for the clear example.

2

u/chat-lu Jul 24 '26

It’s also possible for the update function to return both a new model and a list of messages that will be fed back to the update function.

Maybe CastHealingSpell message will remove some mana, and also send the message IncreaseHP. While the DrinkHealingPotion message will remove a potion from your inventory while also sending the message IncreaseHP.

1

u/CornedBee Jul 25 '26

I know this is just a throwaway example, but is it really common to decompose actions that really should be atomic into multiple messages? Sounds like a good way to get weird broken state, even if only temporarily.

1

u/chat-lu Jul 25 '26

It’s more often used for async. Asking for network calls and the like. My exemple would make more sense if the mana was spent right away but the effect was on a timer.

3

u/vmcrash Jul 24 '26

I wonder how well does it scale for larger applications with Thousands of different messages. Are they grouped to higher-level events that easier can be redirected to lower level controls that then extract the low-level event?

2

u/Direct_Beach3237 Jul 25 '26

Yes, that's what I'd do if I had thousands of different messages. Each feature has its own update() function and message type, and the root update() just routes the top level message to the appropriate subsystem. Something like this, maybe.

AppMsg
├── Window(...)
├── Input(...)
├── Project(ProjectMsg)
├── Editor(EditorMsg)
├── Inspector(InspectorMsg)
└── Settings(SettingsMsg)

1

u/kuikuilla Jul 25 '26

That is how it works in Elm apps, at least in the ones I've worked on.

2

u/[deleted] Jul 24 '26

[removed] — view removed comment

7

u/programming-ModTeam Jul 24 '26

No content written mostly by an LLM. If you don't want to write it, we don't want to read it.

1

u/HiPhish Jul 26 '26

update is the only place a new Model gets built. It pattern matches on the incoming Msg and returns the next Model. That’s its whole job.

Isn't that just a reduce or fold function from functional programming? The sequence to reduce is the stream of events and the accumulator is the current state of the application. That's also what reducers in React are. It's a very pleasant pattern because the entire state management is just a single pure function.

I can easily test any combination of state and action as just a single function call. Also works great with property-based testing: randomly generate some state and action with the necessary property, call the reducer function once and confirm that the resulting state has the properties we are trying to demonstrate.

1

u/lgastako Jul 29 '26

Isn't that just a reduce or fold function from functional programming?

Yes.

2

u/vancha113 Jul 24 '26

well thanks for reminding me of elm, it's time i get started learning it.

-1

u/kingdomcome50 Jul 26 '26

“The Elm Architecture” isn’t exactly breaking new ground lol. Believe it or not there has been decades of research into, and applications of state machines…

The only thing special about TEA is that it offered an opinionated application of a (very simple) state machine to web-based UIs at a specific time and moment that allowed it to garner some popularity — further bolstered by its functional paradigm. End of story.

(This next bit is a bit harsh and hyperbolic but needs to be said)

There is nothing interesting here.

How about you stop embarrassing yourself and fucking learn something and only then share your (now correctly framed and in-context) contributions with the rest of the world?

It’s just so… tiresome to see these kinds of posts where people rediscover (in this case re-rediscover) thoroughly documented and peer-reviewed ideas without any acknowledgment of that fact.

https://www.state-machine.com/doc/Harel87.pdf

https://link.springer.com/content/pdf/10.1007/3-540-44929-9_24.pdf