18 ms·
Okay. I don't want to go too far off topic here but the OP is about F# so I'm certain many enthusiasts will be here, and I have been waiting for this moment to
by kingdomcome50 6y ago
Okay. I don't want to go too far off topic here but the OP is about F# so I'm certain many enthusiasts will be here, and I have been waiting for this moment to get an answer to a very specific question regarding Elmish:
What is the point of the Elm architecture?
I cannot figure out why the `model`, `view`, `update` architecture is preferable to its OOP counterpart. That is, simply defining an interface for each of the above where `IView` and `IUpdate` each contain a single method with the appropriate parameters. That is objectively[0] cleaner!
The current recommendation is to create these ungodly unions for handling the view/update logic. It feels ridiculous and really starts to gum up your modules with implementation logic that is _entirely_ unrelated.
FP and OOP each provide different (opposite) faculties when dealing with the expression problem. FP says[1], "I want to optimize for adding new _behavior_ to the same pieces of data" (one can add functions that operate on the same data without needing to recompile). OOP says[2], "I want to optimize for adding new _data_ with the same kinds of behavior" (one can add classes encapsulating different data that have the same operations without needing to recompile). How does the above square with an architecture built around a set of _single_ functions that operate on different (changing) pieces of data?
The Elm architecture seems to have chosen the exact wrong paradigm (and its associated semantics) for optimizing change! You aren't adding behavior as you build out your application in this architecture. You are adding _data_! The "behavior" is defined exactly one time (in the signatures of the `view` and `update` functions) and doesn't change. I must be missing something... is this really just so we can say "the complier will let us know if we forgot to handle a case"? That seems like a pretty hefty trade-off if you ask me. F# has this beautiful multi-paradigm capability and, in this case, it's been woefully ignored.
[0] Okay fine maybe not objectively, but it certainly seems preferable to modularize this logic.
[1][2] I get it. They don't actually say anything, but the general approach to the EP remains true for most cases.
- reverseblade2 6y agoOP here, it's a great question indeed! Now let's forget about elm/elmish but we are to write a stateful application. It doesn't have to be a web app, just any application needs to hold some state. And that state should be modifiable (Not to mention we will have side-effects as well.) in order to do something useful but we also want to stay on the Functional programming realm to get benefit from things like immutability and other functional goodies. So you see these two goals are contradictory. How FP tackles this state problem? One solution is to use actors and agents. And that's precisely what elmish is. Unlike the conventional apps where you mutate the state directly, an agent in F# is a recursive call which can await for further messages. So just like Flip Flop holds the state in memory, an agent hold the state in a recursive function. And how is this connected to elmish? Let's see how elmish v2 is implemented: https://github.com/elmish/elmish/blob/5330f52153d5181923bb3cb065436748dc38231e/src/program.fs#L90 https://github.com/elmish/elmish/blob/5330f52153d5181923bb3c... You can see an agent there and that's the core of elmish. So basically elm and elmish is a way to handle state changes in a functional manner along with side effect support via commands. I cannot emphasis importance of elmish, because not only it helps for isolating the state functionally but it helps you to keep your business logic separate from UI. So you don't end up messing your code like you use React hooks or context.
- kingdomcome50 6y agoI understand what you are saying. I'm not sure how your points are at odds with the OOP counterpart I describe though. The essence of your response seems to be "because we want to use a pure FP paradigm" to which _I_ would add "despite a clear downside to which F# has the capacity to avoid". If the framework only expects every `Model` to contain 2 methods (`view`, `update`) and never any more (which is the case), I see no reason at all to adopt the Elm architecture. The interface for `update/view/model` is well-defined and un-changing. It's clearly sitting on the wrong side of the expression problem. The program loop could work exactly the same no? Just re-organize: let (model',cmd') = program.update msg state to: let (model', cmd') = state.update msg // I'm not sure where/how program fits in My question is truly as simple as trying to figure out the advantage of the chosen semantics (in F# - I don't know Elm). It can't possibly just be "because we want to stay on the Functional programming realm" can it? Immutability can be achieved in any paradigm. Forgive me if I am coming off overly controversial - I don't mean to be. https://guide.elm-lang.org/webapps/structure.html https://guide.elm-lang.org/webapps/structure.html - Even here in the "MVC" section the documentation _recommends_ you organize your project according to type (containing the `view` and `update` functions). This is very-much akin to my critique. If you reach the point in your project above you have essentially regressed back to OOP (in FP clothes).
- reverseblade2 6y agolet (model', cmd') = state.update msg In order to write that you have to pin a reference to the state. But state is something that has to flow. Sure you can pin it to a ref, make it mutable and change the state but then nothing prevents you to change the state arbitrarily e.g. from a command. I believe yes the answer is because we want to stay on the FP realm. FP imposes some restrictions to keep our sanity. So not having a reference to the state and making it immutable is one of those restrictions.
- kingdomcome50 6y ago> In order to write that you have to pin a reference to the state Forgive my ignorance, but why is that the case? Isn't `state` being passed into the loop? Mutability is orthogonal to the question of where one defines a unit of behavior. Maybe an immutable object is not possible to achieve in F#, but one could certainly imagine the concept of an immutable type with methods. My question is more about the modes of organization: Large unions vs discrete modules and how they affect how one's ability to change a program over time. If you say that the architecture was chosen out of a purest sense of FP, then fair enough. I can understand why someone would go that route. Just had to know.
- pseudonom- 6y agoTo the extent that I understand your complaint, there is a functional architecture that alleviates this concern: arrowized functional reactive programming. https://blog.jle.im/entry/intro-to-machines-arrows-part-1-stream-and.html https://blog.jle.im/entry/intro-to-machines-arrows-part-1-st... talks about it.
- kingdomcome50 6y agoDoes this solve the problem though? I'll be honest, I'm not very familiar with Haskell (and frankly only casually use FP in general), but I don't see how the pattern in your link alleviates my concern. The problem (truly the problem) is about _where_ one defines behavior. It's the expression problem (EP) at its core. A FP approach separates data and behavior in a way that makes adding new behaviors to the same data types easy and OOP combines them in a way to make adding new data types to the same behaviors easy. It's literally the difference between: let model' = update model msg and: let model' = model.update msg Like I said, I don't know Haskell very well (and had a hard time grokking the link), but any purely functional language will suffer from the former over the latter. The side of the EP a language falls on is simply as a matter of the data types/type system available.
- pseudonom- 6y agoAhh, I was focusing on the "local state" vs "global state" bit. Haskell has ways to address the expression problem: e.g. http://www.cs.ru.nl/~W.Swierstra/Publications/DataTypesALaCarte.pdf http://www.cs.ru.nl/~W.Swierstra/Publications/DataTypesALaCa... and http://okmij.org/ftp/tagless-final/course/lecture.pdf http://okmij.org/ftp/tagless-final/course/lecture.pdf