
The Elm Architecture - tosh
https://guide.elm-lang.org/architecture/
======
avolcano
The bit I love about the Elm Architecture that I really wish existed in other
frameworks is the "Command" system, by which the "Update" step can dispatch
further changes. Basically, imagine if a Redux reducer could dispatch further
actions.

I've tried building Redux apps with bidirectional real-time communication (re:
networked video games), and found myself having to pull almost all of the
logic up to the action layer, as I often needed to dispatch further side
effects based on changes that happened as the result of an action. It made my
reducer into nothing more than a dumb setter, and caused me to have to write
complex mocked tests for my action layer instead of my reducer layer.

I know there's been some attempts at bringing this to Redux (such as
[https://github.com/redux-loop/redux-loop](https://github.com/redux-
loop/redux-loop)), but from what I've seen it isn't quite as easy to use.

OTOH, I will say, as an outside observer, it sounds like Elm has run into
problems with this approach (see: the removal of WebSockets from this guide,
pending a rewrite), so I am curious to know more about what issues arose.

~~~
bauerd
>Basically, imagine if a Redux reducer could dispatch further actions.

You can achieve the same by using redux-saga[0], then writing a generator
function ("saga") that inspects dispatched actions, and potentially triggers a
side effect, dispatches another action, waits for another action to get
dispatched in a certain time, etc.

[0] [https://github.com/redux-saga/redux-saga](https://github.com/redux-
saga/redux-saga)

~~~
EduardoBautista
I really wish the React ecosystem had less dependencies. Things that are
included in other libraries are always met with “You can always install X” as
if it were a good thing to add more dependencies.

~~~
wereHamster
Maybe you want to use react with something other than redux, or saga. Now you
say: it doesn't matter, if there is any dead/unused code it will be removed
during bundling. That's indeed true. But then, shouldn't we simply install
_all_ packages that are available on npmjs.com, and never think about
dependencies again? Or don't install any and let our editor or bundler install
dependencies automatically? If installing dependencies happens automatically
as soon as you write an import statement, does it matter anymore whether you
write `import Redux from "redux"` or `import Redux from "react/redux"`?
Hardly…

------
thangngoc89
If you want to use TEA but with easier interop with JavaScript libraries, you
can use ReasonML/OCaml with bucklescript-tea[1] instead

[1]: [https://github.com/OvermindDL1/bucklescript-
tea](https://github.com/OvermindDL1/bucklescript-tea)

~~~
napsterbr
Just pulled an all-nighter deciding what to use instead of Elm. We have a
45kloc codebase in Elm and, despite having some big pros, the closed source
development it goes through, along with the 0.19 debacle, just pushed us over
the edge. The team is very motivated to ditch elm over something else.

So, Bucklescript seems great (and it's wonderful how OvermindDL1 explains in
the open his design decisions. An openness that I've missed in Elm), and IMO
superior than Typescript, and comparable with Fable (in that case being mostly
a matter of personal taste: F# vs ocaml).

But, as long as we are trying new stuff, I've decided to use Clojurescript
with Reagent and re-frame. The tooling around it seems very mature (even more
so than Elm, but I may be wrong), with a good community and macros!

For anyone in a similar position, several good options out there, but the best
two that I've found, in my opinion, are Bucklescript (ReasonML/Ocaml) and
Clojurescript. Cheers!

~~~
skybrian
Why lose sleep over this considering that 0.18 still works? (Genuine question,
not criticism.)

~~~
napsterbr
0.18 works, but compilation takes too long - and apparently that was fixed in
0.19.

We've waited for this fix a long time (over a year), but we can't upgrade
because the only (AFAIK) library that connects to Elixir's Phoenix Channels
stopped working. In fact the language (0.19) currently has no websocket
support.

During this whole year of development, we could not compile the 0.19-dev. It
was somewhere closed. We couldn't even see the commits. Pretty sure there's a
nice and long post somewhere explaining how "outsiders" would slow down the
progress by giving, how dare them, feedback.

We could wait a little bit more, but, even though I was _deeply_ in love with
elm, the language that made me not dread frontend development anymore, I am
now totally uninterested at it, driven away by 1) lack of transparency and 2)
total blocking of genuine and constructive user feedback, which feels like
censorship - or simply a closed-source, proprietary software.

The language is good though. Has some minor flaws, but I believe over time
they will get fixed. Though it lacks some powerful features that will never be
implemented. But ovoverall, we were able to tackle complexity quite nicely. I
guess we - me and my team - just got tired of _how_ it is handled.

Oh, and Elm is way too verbose. Pretty sure that 45kloc codebase will be about
15-20kloc in cljs!

~~~
throwaway237468
> 2) total blocking of genuine and constructive user feedback, which feels
> like censorship - or simply a closed-source, proprietary software.

For those looking for examples, check out the recent post on /r/elm:
[https://www.reddit.com/r/elm/comments/9a0hc6/elm_019_broke_u...](https://www.reddit.com/r/elm/comments/9a0hc6/elm_019_broke_us/)

It was locked and deleted from the front page within hours. There are many
many more such posts that have been deleted. The only posts allowed on /r/elm
are those praising Elm.

They always invite the users to discuss this in the Elm Discourse which is not
constructive because threads about the "forbidden" topics (Native Code) are
either locked or completely deleted within hours.

~~~
rtfeldman
> The only posts allowed on /r/elm are those praising Elm.

The Reddit thread you linked shows many posts that are critical of Elm, so
this can't be the explanation.

As others have noted, you can see what was deleted using this link:

[https://snew.github.io/r/elm/comments/9a0hc6/elm_019_broke_u...](https://snew.github.io/r/elm/comments/9a0hc6/elm_019_broke_us/)

Removed posts included both praise and criticism. What they have in common is
that they broke other rules, e.g. personal attacks.

~~~
throwaway237468
Thanks, I'll let everyone decide for themselves whether the deleted posts were
personal attacks or not. Personally I don't see anything wrong with at least
half of them.

Why do the moderators invite everyone to discuss on Discourse when you know
you'll delete or lock a thread as soon as it's posted?

~~~
rtfeldman
> Thanks, I'll let everyone decide for themselves whether the deleted posts
> were personal attacks or not. Personally I don't see anything wrong with at
> least half of them.

Okay, but "personally I would have only deleted half of those posts" is a much
different claim than "The only posts allowed on /r/elm are those praising
Elm," which is what you said above.

You really shouldn't make such serious claims if you don't mean them.

> Why do the moderators invite everyone to discuss on Discourse when you know
> you'll delete or lock a thread as soon as it's posted?

I understand that you're trolling me, but just for the record - of course we
don't do that.

~~~
throwaway237468
Ok, it's not literally "only" but most. /r/rust is a great example of healthy
community. They only delete obvious troll/personal attack comments. You can go
there right now and post a post similar to what you deleted on /r/elm and I
bet it won't be deleted or locked.

> I understand that you're trolling me, but just for the record - of course we
> don't do that.

I'm not trolling. I'll bet $100 that you've at least deleted 3 threads about
native code and locked at least 5. Latest example: [https://discourse.elm-
lang.org/t/elm-0-19-released-what-has-...](https://discourse.elm-
lang.org/t/elm-0-19-released-what-has-changed-regarding-native-event-
modules/1670)

Moderator quote:

> Finally, a moderation note: I’m going to lock this thread since discussions
> on this topic have the unfortunate tendency to spiral out of control and we
> have already debated this past the point of productive return. If you have
> things you need to upgrade, we’re of course happy to help… let’s just do
> them in separate threads. :slight_smile:

Please don't suggest users of locked Reddit threads that they're welcome on
Discourse. Just say the truth - you don't want any discussions about this
anymore. There's no harm in being honest.

~~~
rtfeldman
I see where you're coming from!

To me, those two mod notes are consistent and sincere. Both are saying "this
particular topic has been debated to the point where discussions no longer
make progress, they just take up people's time and energy. That said, by all
means please feel free to start a new thread about something more specific if
you'd like to discuss that."

I see this as analogous to React and JSX. When React came out, many people
said "putting HTML in JavaScript is Wrong, and React shouldn't do this."

At first, whether React should use JSX was a reasonable thing to debate. In
2018, this has long since been settled. A 2018 thread arguing that React
should abandon JSX is going to take up people's time but it's not going to
change the outcome of a decision that was settled years ago.

This is how it is with things like Native and typeclasses in Elm. Maybe you
disagree, but I think there comes a point where it's reasonable to say "it is
not a good use of everyone's time to re-litigate an issue that has been
settled for years. You may still disagree with the final decision, but that
ship has long since sailed."

I can understand the argument that "people must be free to waste everyone's
time, because anything else is tyranny" and I can also understand the argument
that "the Internet is full of places to post whatever you please, but _this_
forum is focused on collaboration, sharing, and learning, not wasting time."
It seems reasonable to me for a given forum to embrace either one of those
moderation philosophies.

This is why I see the two mod notes as consistent and sincere. They are both
saying "these broader design decisions have long since been settled, and we're
locking the topic because they are a magnet for contentious discussions that
don't go anywhere. That said, you are genuinely welcome to start a fresh
thread about a more specific topic to your particular situation."

Again, maybe you disagree with this moderation philosophy, but it's simply not
true that mods are inviting people to post only to lock whatever the follow up
would be. That would be ridiculous.

~~~
throwaway237468
Here's a quote:

> If you'd like to discuss Elm's trade-offs, both pro and con, there are
> plenty of people who are willing to talk openly about them in a calm way.
> I'd recommend opening a thread on Elm Discourse if you're interested in
> that.

Please edit it to say that discussions about cons like native code will not be
tolerated and will be locked or deleted without warning.

------
zoul
I like the Elm architecture so much we have tried to do something similar in
Swift on iOS. It’s a shame it’s not possible to go all the way in (it’s quite
hard to find an analogy for the DOM diffing view part), but after a year of
work or so it looks like the principled state management + message-based state
changes + principled side effect modelling brought us about 70–80 % of the
advantages. It feels _so_ good so see the state of the app change using well-
defined messages and not by ad-hoc state changes on some entangled forest of
singletons. I look forward to experimenting with the architecture further. I
can’t recommend it enough.

------
Touche
So what ties together the model, update, and view? It looks to me like it is
this Browser module, is that right? Not knowing Elm or Haskell syntax, I'm
guessing that Browser.sandbox is a function you call, passing in the model,
update, and view functions.

Assuming that's all correct, how does Browser.sandbox, work exactly? In
particular how does it know when to call Update?

At first glance it looks like it might massage the view and detect event
listeners such as onClick in the button example, is that correct?

~~~
hawkice
Turning Html Msg types into actual Dom nodes means hooking up the event
handlers so that they call update with the Msg the event handler is given.
Browser.sandbox is building the Dom nodes and has the update function.

~~~
Touche
Yeah, I got that far. How does it know what it should turn into an Update
handler though?

~~~
neoberg
Browser.sandbox, Browser.application etc. are your app's runtimes. (Or they
are the doors to the runtime)

Subscriptions or DOM elements can generate Msgs and every Msg causes an update
cycle.

------
wa1987
In what ways does the Elm architecture differ from MVC?

~~~
m90
Not the Elm Architecture, but I found this a very good article on how MVC
differs from unidirectional approaches:
[https://www.futurice.com/blog/reactive-mvc-and-the-
virtual-d...](https://www.futurice.com/blog/reactive-mvc-and-the-virtual-dom/)

~~~
mpweiher
As usual, that article gets MVC wrong.

In MVC, the model-view communication is not "interactive" as defined by the
article (source pushes to sink). Instead, the view pulls data from the model.
There is only a general #changed notification to let the view know that the
model has changed.

So "unidirectional" approaches fix the problems you get when you don't
understand MVC. ¯\\_(ツ)_/¯

~~~
ricardobeat
The problems lie in the way change notifications are managed. Any reasonably
complex client application will have multiple data sources feeding into a
myriad of components, with a very entangled dependency graph. In MVC, you’ll
start sharing models and notifying changes selectively, with a lot of “action
at a distance” from component interactions triggering updates. This is usually
hard to visualize and debug (worst case, recursive update loops).

In Flux/redux the unidirectional nature comes from all updates targeting and
emanating from a single source (store), and being routed to views accordingly.
An interaction from one component will never result in a disconnect where you
failed to update the correct model(s) or to notify the right components of a
change (hi Backbone.js).

~~~
mpweiher
> Any reasonably complex client application will

> have multiple data sources feeding into a myriad of components,

Why? Certainly my MVC apps have a single "store" that's the source of truth,
and I would say that what you describe is _not_ following the MVC pattern.

~~~
ricardobeat
You're gonna have to provide a bit more context for that. What part of the MVC
pattern?

~~~
mpweiher
Various parts. First, the M->V communication should only notify, the View
decides if and when to refresh itself, and the action it takes is _only_ to
refresh itself.

"In this metaphor, views deal with everything graphical; they request data
from their model, and display the data."[1]

Also, the View should only make a note that it needs to be refreshed (Cocoa
for example has the convenient "needsDisplay" boolean property on Views), not
actually do the refresh on notification. I see the latter a lot, so you have
the model effectively calling refresh on the view, so pushing updates. That
ain't MVC, in MVC the View pulls from the model.

"The standard interaction cycle in the Model-View-Controller metaphor, then,
is that the user takes some input action and the active controller notifies
the model to change itself accordingly. The model carries out the prescribed
operations, possibly changing its state, and broadcasts to its dependents
(views and controllers) that it has changed, possibly telling them the nature
of the change. Views can then inquire of the model about its new state, and
update their display if necessary."[1]

Once you're down with pushing updates, you tend to also see "optimizations"
that also push the data to the view (very much what the linked article
describes). Again, not MVC, View pulls, model doesn't push.

If the view only refreshes itself and pulls from the model when notified, it's
hard to get an entangled dependency graph, and downright impossible to get
loops.

So if you get loops, you're definitely not doing MVC.

As to the "multiple sources" feeding into a "myriad of components", that also
doesn't quite match. In MVC, all sources feed into the model. When the model
changes, it sends its change notification, and it doesn't really matter where
the original change came from, the action afterward is the same. So if
"multiple sources" are causing you problems, that is a strong indicator that
you are not doing MVC, and "myriad of components" also indicates that you
don't have a strongly developed model.

[1]
[https://web.archive.org/web/20100921030808/http://www.itu.dk...](https://web.archive.org/web/20100921030808/http://www.itu.dk/courses/VOP/E2005/VOP2005E/8_mvc_krasner_and_pope.pdf)

------
andrejewski
If anyone wants the Elm architecture in JavaScript there is Raj:
[https://jew.ski/raj/](https://jew.ski/raj/)

I spent over a year building the framework and a set of libraries around it so
that the architecture feels natural to JavaScript developers.

There's only a few of us building apps with it right now, but the stories for
doing most anything are covered and as of 2 months ago the framework hit 1.0.
It is a safe foundation to build on as I don't plan to ever change it.

------
dom96
Huh, I must say I thought there was more to Elm's architecture (I've seen it
mentioned in the recent negative article about Elm).

I've written an SPA[1] recently in a framework that is quite flexible and also
ended up using a very similar architecture.

1 - [https://github.com/nim-lang/nimforum](https://github.com/nim-
lang/nimforum)

------
vmchale
This is just model-view-controller except Evan is taking credit for inventing
it.

------
sridca
Yesterday's submission on Elm with interesting discussion in comments:
[https://news.ycombinator.com/item?id=17842400](https://news.ycombinator.com/item?id=17842400)

