DEV Community

Cover image for Exploring The F# Frontend Landscape

Exploring The F# Frontend Landscape

Angel Daniel Munoz Gonzalez on May 24, 2022

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...
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.

Collapse
 
kentechgeek profile image
Ken Okabe • Edited

This article is remarkably versatile, cutting-edge, deep and tidy! Truely Amazing work. Thank you.

Collapse
 
merthod profile image
Merthod

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?

Collapse
 
tunaxor profile image
Angel Daniel Munoz Gonzalez

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 answers

Collapse
 
merthod profile image
Merthod • Edited

Thanks for the thoughtful reply. Was it long util you got good in it?

Thread Thread
 
tunaxor profile image
Angel Daniel Munoz Gonzalez

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

Collapse
 
kaashyapan profile image
kaashyapan

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.

Collapse
 
tunaxor profile image
Angel Daniel Munoz Gonzalez

What is your go to for client SPA routing ?

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 wish you had mentioned Fable.Store.

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!

Collapse
 
frodolight profile image
Frodo

Very valuable article

Collapse
 
tunaxor profile image
Angel Daniel Munoz Gonzalez

Thank you! I appreciate the kind words!

Collapse
 
lyndon_gingerich profile image
the-not-mad-psychologist

Excellent article, but the lack of proofreading makes it hard to read.

Collapse
 
tunaxor profile image
Angel Daniel Munoz Gonzalez

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!

Collapse
 
ambriel profile image
Ambriel Goshi

Thank you for writing this

Collapse
 
tunaxor profile image
Angel Daniel Munoz Gonzalez

Than you for the kind words!
hopefully it helps people out :)

Collapse
 
digitalstoic profile image
Mat • Edited

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?

Collapse
 
tunaxor profile image
Angel Daniel Munoz Gonzalez

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

Collapse
 
jkone27 profile image
jkone27

Updates on the top 3 in the end of 2023, just out of curiosity @tunaxor : ) great article