Hello everyone it's been a while!
Today we will talk about what is the current frontend landscape of Frontend development for the F# ecosystem. Ove...
For further actions, you may consider blocking this person and/or reporting abuse
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.
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
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
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
No problem. I am happy to contribute something to the article.
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.
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.
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.
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)
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.
I agree it is the only way to truly go functional
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.
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 concernPerhaps 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
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)
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
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.
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.
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.
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.
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.
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.
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.
This article is remarkably versatile, cutting-edge, deep and tidy! Truely Amazing work. Thank you.
Very valuable article
Thank you! I appreciate the kind words!
Hi, I'm from planet JS/TS. Can you tell me in a nutshell why you do like F# for front-end so much? Or can recommend some resource?
Hello @merthod thanks for asking
The primary reason I like F# in general is that is a language that has helped me be a better programmer, it has improved the way I see and work with code, taught me how to program in a safer way (i.e. prevent null/undefined from even being part of the workflow), how to write smaller and more meaningful functions, how and when to use mutability and when to hide it so consumers can still program in a safe way. Overall it is a fantastic language that helps me and my brain to write better, safer and simpler programs with less effort and resulting bugs.
In the case for Frontend development, it is due to the reasons above and for the same reason some people like to go full-stack with JavaScript using a language you're proficient and that has type safety waranties as F# is just a very good option.
If you like typescript because it helps you write safer code and avoid many javascript's dynamic nature pitfalls and similar features, then F# is a very good language to check out
if you want to have a check on the language itself I'd suggest to read my other articles on F# specially this series, there's also this wonderful book zaid-ajaj.github.io/the-elmish-boo... which can be of use to learn more although it might be a little bit outdated in some areas but regardless it is a very good resource
on twitter there's a whole community aroun the
#fsharp
hashtag, there's an official F# slack you can check fsharp.org/guides/slack/ and also we have an unofficial discord chat discord.gg/R6n7c54 where you can ask questions about the language and someone will try to give you some answersThanks for the thoughtful reply. Was it long util you got good in it?
I can't say an exact number for sure, it took a while because I had a really hardwired OOP (Java style) thought process but since I was just doing projects and writing code here and there for fun one day I was already doing bigger project.
However within 3 months I already felt quite productive with F# even if I wasn't good at it so there's that
What is your go to for client SPA routing ?
For my last pet project I went with github.com/fable-compiler/Fable.Store with svelte and routify for routing.
I wish you had mentioned Fable.Store.
For my next if I can manage to avoid js deps and the fear of needing js deps in the future, I might go full blazor. I love the router infer.
When Blazor was still young, I remember splitting stuff into mini MVU 'components' and passing messages between components via AsyncRx. It was a scalable model.
I would normally use whatever the framework brings (Feliz.Router in case of Feliz, Fun.Blazor has its own, bolero has it's own) and if there's not a specific library in the framework I write some custom bindings over navigo on the projects that I need it because it is fairly small and does what I need most of the time.
For personal projects I use a more simple approach, I use DU's and pattern matching to just show what I need to show as if it was like a router
I did mention Fable Store although, with a different name (Fable.Svelte)
I haven't seen much movement around it otherwise I would for sure talked more about it it would be nice if you could also write about how you're using it and why did you chose it!
Thank you for writing this
Than you for the kind words!
hopefully it helps people out :)
Excellent article, but the lack of proofreading makes it hard to read.
Thank you for taking the time time and effort to read it :)
I agree completely with you sometimes I wish I had more time to do these things but if I don't get them out when I finish those it is hard to know when I'll have time to publish them I will take more efforts to improve in this area, thank you!
Great article, thanks. 1 question about PWA (Progressive Web Application): Only the Fable-based solution allow the required low-level setup (service workers, data cache, etc), right?
PWAs are agnostic to how the application is built or how it runs, so you can build PWAs from all of the tech mentioned on this post :)
that being said, of course if you use a toolchain that allows for more control over service workers and similar things (like webpack/vite which are usable on the fable side) it will be easier to include it in your source code in F#
otherwise you will have to write that other Javascript manually write your service worker in javascript like I did here
So if you choose websharper/fable/web assembly you can for sure
actually, here's a sample I wrote with bolero last year
Hi, I've developed a new F# frontend framework called VanFS:
1:1 bindings from F# to VanJS (an ultra-lightweight , zero-dependency , and unopinionated Reactive UI framework based on pure vanilla JavaScript and DOM without React/JSX) + WebComponents + FRP.
Check it out, please.
Updates on the top 3 in the end of 2023, just out of curiosity @tunaxor : ) great article