DEV Community

Discussion on: Exploring The F# Frontend Landscape

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

Great article and nice overview!

One omission. You can use "plain" Elmish, with Fable translating to JS, and React for DOM rendering. I only use React as an HTML DSL. So view code maps 1:1 with the rendered HTML, but with the power of F# (conditionals, loops, etc). And no extra concepts like hooks, stores, components, etc. Though not without its faults, I find it preferable and much simpler than the options presented here.

I wish there were a HTML DSL on top of a Svelte-like (memoization) renderer. It would be a great alternative to using React rendering. As it is, I think Svelte (and Sutil) rendering is tightly coupled with its bespoke component-based pattern.

Collapse
 
tunaxor profile image
Angel Daniel Munoz Gonzalez • Edited

Thank you for making the time to read the posting I'm very thankful!
The main reason I do not include Elmish by itself is because every option presented here supports it either alone or together with better and simpler state management solutions.

And because I do not think Elmish alone is actually good for most applications, a couple of counters here and there is good but when you have to handle multiple pages or isolate state in one of your pages it becomes a burden rather than an advantage.

I've written my fair share of Elmish apps myself and I always have favored other options because they are more flexible, or in combination with Elmish they become more powerful

I only use React as an HTML DSL. So view code maps 1:1 with the rendered HTML, but with the power of F# (conditionals, loops, etc). And no extra concepts like hooks, stores, components, etc.

Every option listed here does the exact same thing with or without React and if required all of them can be used just Elmish rather than the other state management solutions

I wish there were a HTML DSL on top of a Svelte-like (memoization) renderer. It would be a great alternative to using React rendering. As it is, I think Svelte (and Sutil) rendering is tightly coupled with its bespoke component-based pattern.

That's Sutil for you, Sutil does not implement any kind of component based style, Sutil is just functions and DOM elements the same can be said for Fable.Lit but that's not an F# based one

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

No problem. I am happy to contribute something to the article.

every option presented here supports it either alone or together with better and simpler state management solutions

All the examples use components (objects) in the large and Elmish in the small. This definition of "supporting" Elmish (aka MVU) is quite different from the functional-style UI option I mentioned. Hopefully you can see why I considered that (no functional style UI in a functional-first language) an omission.

And because I do not think Elmish alone is actually good for most applications, a couple of counters here and there is good but when you have to handle multiple pages or isolate state in one of your pages it becomes a burden rather than an advantage.

My experience is the opposite. I used various component frameworks for many years. They were easy to get started, but always became exponentially more difficult to maintain as the apps got larger. And for mostly technical reasons around object / framework conformance. Then I found MVU, which has the opposite problem -- high initial overhead (esp. wiring), but ever smaller increases to overhead as the project grows.

We've used MVU for the last 6 years, and for more than a couple of counters. Of the 10ish UI apps, the largest F# one has ~50k loc and 125 pages, many are page fragments, various depths. The wiring overhead has advantages too: code is explicit, traceable, functional, and free of framework-proprietary concepts. That's not to say MVU is perfect or even fully baked. (Is anything?) And I know too well that it's a hard transition from years of practice with components. But it is a very effective option, and the only one I've seen that is capable of using deterministic functions all the way down. I'm assuming benefits of those don't need to be enumerated to an F#/FP audience.

Sutil does not implement any kind of component based style, Sutil is just functions and DOM elements

So I don't have to use binds, stores, lifecycle events, or any componentization things? Aka, I can use "pure components", all module (static) functions, no object constructor, and it still does memoized rendering? I hadn't seen any examples of that. Based on the examples, the memoization seemed inseparable from its component-oriented design.

The other issue with Sutil for me is Feliz. Perhaps I am cynical from years of component frameworks that hide things which bite me later. But I prefer to code against the web tech. Structurally, Feliz is not quite that. And it mixes in separate concerns like hashed CSS, animation. I'd prefer to compose different concerns with library approach. And if custom coded, represented like it will appear/operate in the web tech. This keeps my code tied to long-lived/backward-compatible web standards rather than bespoke, transient framework abstractions.

Thread Thread
 
tunaxor profile image
Angel Daniel Munoz Gonzalez

Hopefully you can see why I considered that (no functional style UI in a functional-first language) an omission.

I see why some people like to go full functional and I think I understand what you mean with that.

I'm glad that MVU is working for you and your company on that scale I've always found it hard to grasp when things grow, specially because once you grow a certain amount you have to start abstracting types and function signatures for it to keep it readable (continues on the next section)

The wiring overhead has advantages too: code is explicit, traceable, functional, and free of framework-proprietary concepts.

This generates IMO some indirection that I can't really handle cognitively and many others I've known who have left elmish like abstractions behind like elmish itself, redux and similar patterns.
Even the react ecosystem itself was primarily using Redux but the industry moved on because not everyone was able to work on these architectures alone it still has it's use cases but it is not in vain that different alternatives surfaced to avoid going full elm style, granted that I'm biased against a pure functional approach as well so those things could be tied to that.

But it is a very effective option, and the only one I've seen that is capable of using deterministic functions all the way down. I'm assuming benefits of those don't need to be enumerated to an F#/FP audience.

I agree it is the only way to truly go functional

So I don't have to use binds, stores, lifecycle events, or any componentization things? Aka, I can use "pure components", all module (static) functions, no object constructor, and it still does memoized rendering?

One thing that looks like is kind of an omission here is that react is a rendering library that works with components even if you use it in a static way you are using components underneath that's how every element is working so I don't think using Feliz, fable-react, lit or any other F# alternative will actually get you away from components.

back to the question, as it is stated on your question No, you still have to use a store, this store is backed by your elmish model though you can see a full example here . The reason you need to bind fragments here is that rather than memoizing Sutil functions only run once (so there's not even need for memoizing) and only those fragments will change. This introduces you to maybe 1-5 framework concepts so this breaks what you are looking for I guess. In any case underneath of it (just like react) it will do what it needs to print everything on screen while it offers component-like features (just like react) you don't need to use them.

The other issue with Sutil for me is Feliz. Perhaps I am cynical from years of component frameworks that hide things which bite me later. But I prefer to code against the web tech. Structurally, Feliz is not quite that. And it mixes in separate concerns like hashed CSS, animation.

There's a miss understanding here, Sutil uses Feliz.Engine as explained in the article it is a library that abstracts HTML representations into the types that a rendering library may need to construct Virtual DOM elements or DOM elements, Feliz.Engine doesn't do anything related to hashing css or "scoped css" or anything like that the CSS part of the engine is abstracting types that can be used to represent standard CSS it's up to the library user to decide what will be done with that css it can be used to produce stylesheets or even inline css strings but that's is the library's consumer concern

Perhaps you might mean the original dialect Feliz (the library not the engine) which rather than HTML you're right is not entirely that is just an abstraction over React dialect of html

I'd prefer to compose different concerns with library approach. And if custom coded, represented like it will appear/operate in the web tech.

That's what Sutil did with Feliz.Engine it used the library to compose raw DOM elements that can be mounted on any HTML element you can't get closer to web standards than that (using raw DOM elements)

This keeps my code tied to long-lived/backward-compatible web standards rather than bespoke, transient framework abstractions.

Agreed that's what both Sutil, Fable.Lit build on top just HTML and web standards, arguably Solid could fit into this category as well but granted that one has more things going on.

I think this also brings other things to the table, I think the main reason elmish gets relegated to lesser usages than the mainstream is that you can accomplish similar things with less verbosity and I agree that you lose the benefits of traceability and explicitness that elmish provides but the end result and time invested is what often matters

Now I will reiterate that all of these support Elmish and you can go just as full elmish as elmish + fable-react (which I think is the version you're speaking of) with a couple of extra concepts or a mix between majority elmish minority components or majority components minority elmish or just plain components or plain elmish

I'd think the best way to see how do they fit on your scale is to try their elmish support (if you are ever looking for an alternative which sounds like you're fairly comfortable with your setup and that's great no need to change) and give some feedback on what you think they're lacking to have Full Elmish support

Thread Thread
 
kspeakman profile image
Kasey Speakman • Edited

This generates IMO some indirection that I can't really handle cognitively and many others I've known who have left elmish like abstractions behind like elmish itself, redux and similar patterns.

I think I understand what you mean. Early Elm patterns had a child message pattern that was very tedious and hard to resolve some needs. Elmish's implementation of the Cmd and subscription signatures, though they are quite logical and flexible, always feel like a mental obstacle course to work thru. As I alluded to earlier, I don't think MVU is quite complete as yet. It needs more investment. I had to augment it with at least 1 new core concept and some add-on patterns to make it work for us.

Even the react ecosystem itself was primarily using Redux but the industry moved on because not everyone was able to work on these architectures alone it still has it's use cases but it is not in vain that different alternatives surfaced to avoid going full elm style, granted that I'm biased against a pure functional approach as well so those things could be tied to that.

There are some other factors. Redux author became a public face of the React team. Redux has a similar incompleteness problem to MVU. But I think the core issue here is that it is VERY hard to change out of an object-based mindset. At least in my experience. It took years / multiple attempts to chip away at my deeply ingrained OO-shaped instinct on the how to code. I thought I was there. Then after my first significant Elm project, I realized I still hadn't got it. That's when I finally started to understand functional patterns. So I know first hand that sometimes I just can't break my brain anymore and I go back to what I know. Conversely, the 3 devs I've trained with no previous programming experience were usefully contributing MVU UI code after about a month.

One thing that looks like is kind of an omission here is that react is a rendering library that works with components even if you use it in a static way you are using components underneath that's how every element is working so I don't think using Feliz, fable-react, lit or any other F# alternative will actually get you away from components.

Not an omission. It uses components when you take a complete xray. But the view code that I write is still deterministic and structurally similar to the rendered DOM. Except for event handlers, which are minor holes in both, but still look like an HTML syntax. Since we only use React in this way, the team only has to know HTML, no further knowledge dependency to unlearn when the next better thing comes out.

No, you still have to use a store, this store is backed by your elmish model though you can see a full example here .

Yes, this is a side effect when my goal is deterministic code for logic and view generation. Loading a store is not much different conceptually from passing it as a function argument. I prefer the latter because there's no hidden magic. I've been bitten by magic approaches many many times. They're never quite magic enough to cover all things I run into. Then I have to work around or against it to accomplish what I need. That kind of overhead is very costly in the long term.

There's a miss understanding here, Sutil uses Feliz.Engine ... Perhaps you might mean the original dialect Feliz

Yes, the last Feliz I researched was the library. (Regarding the scoped css, animation.) The Engine version syntax is a further step removed from the rendered HTML. It's less cognitive load when it matches more closely.

// Fable.Elmish.React
div [Class "foo"] [
    str "bar"
]
Enter fullscreen mode Exit fullscreen mode
<!-- rendered -->
<div class="foo">
    bar
</div>
Enter fullscreen mode Exit fullscreen mode

Now I will reiterate that all of these support Elmish and you can go just as full elmish as elmish + fable-react (which I think is the version you're speaking of) with a couple of extra concepts or a mix between majority elmish minority components or majority components minority elmish or just plain components or plain elmish

I don't agree here. A couple of extra concepts and mixing in components, if it requires you to use its code abstractions, is a framework. "Full" MVU is not that. (I'm not at all minimizing MVU in the small. It is a great tactic.) MVU is a library approach where I opt into library abstractions. So I can choose a different renderer for example, without changing my page logic. (There used to be an alternative renderer for Elmish.) With MVU the functions and types I use are only the ones I defined. (I don't use Elmish's Cmd. Dispatch is in terms of my Msg type. And the Elmish-specific stuff is only called once at app startup.) The extra knowledge dependency of MVU is the pattern, which is very different from depending on framework abstractions in each page. Invariably a framework author learns better ways to do things. And abstractions change. I've been in situations where it was simply not feasible to upgrade. Then all ecosystem efforts go to the new version, and the app stagnates, and the team wants to rewrite it. This is perhaps a cost we don't consider when choosing a component-oriented (aka object aka framework) approach. (Actually there are more costs, like systematically training devs to prematurely abstract, which creates a large maintenance cost long-term.) I guess we assume it's the cost of doing business. But I can avoid a huge chunk of that with a functional architecture.

I'd think the best way to see how do they fit on your scale is to try their elmish support (if you are ever looking for an alternative which sounds like you're fairly comfortable with your setup and that's great no need to change) and give some feedback on what you think they're lacking to have Full Elmish support

Hopefully the above explains what is needed to support functional architectures like MVU. (No required usage of objects, no systemic code abstractions that user code needs to depend on, nor required side effects in logic / view calculation. Basically, recipes composing single-purpose libraries rather than frameworks are needed to support functional architecture.) But I don't think popular projects will be looking at this as a primary use case, because most of us devs are not interested. We just want a familiar framework approach that "gets it right this time". It's an apt description of me for nearly 2 decades.