throw868788
We've created very large scale applications in it requiring significant throughput. One of its benefits is also its disadvantages - we typically are quite productive in it and so don't feel the need to hire as many people in the team. Before LLM's were a thing we found we were wasting less time with boilerplate in general than our C# code for example. YMMV.
I personally don't have much issues using ASP.NET directly from F# (don't bother with Giraffe or anything like that); its not too hard. The routing layer is only a few classes/functions anyway - it isn't the majority of your program. I guess maybe I'm in a different position to you - I find for the few bits of ASP.NET config it isn't worth switching to C#. A few rules you need to follow but I would say that is a lot less than the Clojure learning curve and you get static typing still - that is just my opinion however.
My experience is that both languages have their pros/cons and tbh both have their challenges w.r.t adoption. My personal view is most of these challenges are not technical. The issues we complain about with the language as developers are IMO minor (i.e. things are still possible or decent compared to some other languages) and probably aren't the major factor in the wide scale technology choice away from FP in general.
For better or worse developers are often "languages takers" not "language deciders" in most big shops and this is where most of the jobs are - its only in niche elements/problem spaces (i.e small shops, dedicated niche teams, etc) where niche technologies can be worth it. This means not a lot of jobs; but when there is a job it can be higher paying. Big employers drive the market for technologies though when it comes to large scale usage and they want commodity fungible developers (i.e. resources), and are happy using a mediocre tech that gets the job done to do it. Unless there is a "killer app" where the language must be used to unlock the value most managements will stay clear of better tech - predictable, boring, commodity tech that I can get anyone in for (contractor, offshore person, etc) wins for them every time especially for the MBA/product manager/etc types. In the current economic climate developers are trying to meet the jobs that are being advertised as well (higher interest rates, less startup's, etc etc).
However to be honest anecdotally I do think even if they aren't going down in user terms, FP languages are losing percentage market share. From my personal experience FP is no longer new; the hype of FP in general has died down. The hype has moved onto other things over the years (e.g. AI, blockchain, bare metal technologies that enable the above, etc etc).
I'm not saying FP doesn't have a lot of value. I enjoy, particularly when the program calls for it, FP languages like F#, Scala, etc. They still offer a lot of value IMO for all the usual spouted reasons.
IMO I just think the hype has moved from technologies that do things better (i.e. more efficient, less error prone, makes my job easier, etc) to technologies that do shiny new things. There are a lot of reasons for this, many of them aren't in fact technical but economic and political (e.g. management). Things like less jobs globally in tech (meaning my skills need to have wider reach), demand for specialist roles that aren't as language focused, etc play a part. Mainstream languages taking FP features as well makes the return vs risk of using a FP language even worse of a tradeoff - there's less perceived return for changing stack than before.
Maybe it doesn't need to thrive for everyone; maybe it just needs to continue being useful for the people who employ it and add value. That's probably OK. They could be just busy building stuff instead of blogging, especially if the community is mostly compromised of senior developers (10 years +).
This is in contrast to Java which for a significant time wasn't showing any evolution which sparked many alt langs on that platform. Sometimes things succeed but silently, and sometimes things that should succeed just don't and maybe there isn't a real explanation. In fact I think the latter across most products is actually the most common outcome, not just in programming languages.
TL;DR I agree with the article, it suffers from an adoption chasm. Pain and herd safety are important especially when people are considering their career progression.
To your dot points:
* I think F# has .NET which is a "proven" and good enough ecosystem as well. You won't get stuck by picking F# even if it means using a C# library. Have to do the same in Scala/Kotlin and the like typically as well.
* Editor has caught up, my surprise is that you can write with Vim and kinda get away with it - only find that works in terser/scripting like languages anyway unlike C#. It has wrappers and libraries for doing things in a more F# FP manner if you wish although when I was doing F# I avoided them (i.e. I avoid things like ScalaX most of the time too).
* F# has a good scripting story; I've seen F# scripts used quite often by people even if coding in C# to check things and REPL program.
* These days F# just works too mostly. `dotnet new webapi/console/etc -lang F#` and you have a webapi for example.
* There are F# libraries around for similar things (Http, Collections, Async seq, etc). They are probably maintained to the level they realistically need to be, I would argue they still do their singular job quite well. At some point a library just does what it needs to do.
There's things I prefer in the .NET ecosystem as well - value types, inlining, etc.
I think that both approaches solve a lot of the same problems, but there is some problems that fit one paradigm better than the other and I don't think they are entirely mutually exclusive either. I've heard it mentioned with Loom it solves the async "colour" probglem however I think people "overblow" the color problem of the Async API too much. If most things are async it isn't really that painful. I actually like when something is marked "async" and I have to be careful how to use it. How is it async? Can the code run in parallel and join later? Is there race condition potential? Deadlock? How is it actually async, what's it sync vs async behaviour? How do I propogate the need to halt the async operation across multiple layers? Who owns the whole workflow, can I make sure the async context is propagated to that layer so they can cancel it for example?
Probably an unpopular opinion but knowing a method has async behavior, at the type level, could be a feature not a bug. It forces you to handle it at the sync/async junction and think about these things.
The usual features (e.g TP's) that are used to sell the language though I feel are oversold and tbh not the main reason F# users like the language - in fact I think TP's need an overhaul and are a distraction. Inlining code for math (which is just getting an equivalent like feature in the next C#), unions/records together, functions and type inference, leaning to compile time vs runtime dispatch in code style, etc I feel are where its strengths are at. More than features I've found for large scale projects its just easier to spot bad code in F#, less bugs have made it ot Production when .NET teams have tried it, and less of it makes it to code reviews.
I've found F# tooling at least for VS Code more stable than the C# equivalent. But that's not saying much - most languages plugins for VS Code don't feel that stable to me if not JS (e.g. the Java one used to crash on me all the time). In VS Studio, Rider, etc are options.
Documentation should be improved sure especially for people getting into it; but it is the smaller language and documentation goes out of date quickly. This obviously penalises the new starter without a mentor/senior dev to teach them in the job/elsewhere.
I don't think F# will ever be more than niche; that I can agree with you. Not because of any technical reason though; perception and marketing unfortunately does matter. Your points around "investment", etc are to me impressions/metrics around that.
In the end these things are just tools. I'm personally in a team that is doing a lot of generic math, and in our .NET based projects F# seems easier and quicker to get that performance. I know C# preview adds some improvements here but it seems more complicated than the F# approach.
- Domain modelling, algorithm, business logic, etc is easier with F# in general. C# is getting better than this, but F# is there and has been there for a long time.
- Integrating with build tooling and generation tends to be C# because these tools were designed for that target (i.e not the language itself). The value is in the tooling and the engineering in that - C# just happens to be the target.
Personally with the above I've found most of the logic tends to be in F# with some C# projects for where the packages needs that build tooling support (i.e. not the language or syntax, but for the tooling or other features of Roslyn). An example would be Grpc.Tools. Most of the time these projects can use generated code anyway so the writing of C# can be kept to a minimum - i.e. not one single C# file in the C# project. Besides I think the learning curve/barrier for a language isn't really syntax, or language features - its the libraries to use, the patterns, the ecosystem, package manager, CI/CD settings, etc. Using F# isn't a large cost once you've learnt all those things that are shared which is way more than isn't.
F# feels like a different dev workflow than C#, which IMO is a very good thing in F#'s favor. It feels more like coding JS, Go, etc to me with static typing and richer features. I also think EF, as it is designed, doesn't lean to the FP approach that well.
I personally don't like EF - I'm happy with something in .NET like DbUp for migrations and straight SQL. Normally I get better performance anyway doing this and in the age of microservices I feel this pattern actually makes it easier to change DB's if you need to (but I still assert you probably never will without a rewrite most of the time) - just use the different query language and port your data access layer. Move to Redis? Just port your F# module that queries the DB in SQL to Redis code. In F# it also allows a richer data modelling experience doing it this way (e.g use of DU's for modelling cases in your domain) since db logic is decoupled from your domain types.
I never believed my domain model had to look similar to my DB table design which is what EF typically encourages. Especially with the modern features of DB's like Postgres you are leaving more and more performance on the table. In the apps I've written doing that leads to lower DB performance than otherwise, sometimes for some quite trivial apps.
TL;DR: Tooling like Resharper, designers, etc is nice but often is there IMO because the language itself is bloated and not expressive enough to just state your original intent there succinctly. If the code is enough then F# can express pretty much what C# can.
- Templates are no substitute for intuitive and minimal code for a number of reasons. (e.g. how to extend, how to customize, confidence that it isn't brittle if I change one thing, etc etc). If a template doesn't have the exact variation you want (e.g. a faster logging library) the learning curve is very high for a newcomer .NET dev. NOTE: I think this applies to some other OO languages too.
- Suggesting popular packages and namespaces is a great tooling improvement. But it could also imply a closed wall garden. The fact that other non C#/Java like languages don't have this problem even with something as minimal as VS Code as your IDE shows that the tooling is trying to cover over a language and/or library problem. With the Java/C#/other old OO lang workflow you need the knowledge you need to use the library itself (convention based, which class, which extension method, which namespace for that object to configure this feature, startup class conventions, etc). It isn't discoverable even with the packages imported. Remember just getting Serilog working in new ASP.Core and it taking a day or so with all the packages, config, etc etc if you don't already know what you are doing.
I like the improved docs. But fundamentally simplification/minimization is better. I think the dev workflow that means I don't need these tooling improvements is still a better experience. I like .NET, but I do understand when using other languages they seem to not have these issues despite not having the uber tooling that C# has. They don't have package suggestions, they barely even have auto complete yet the library design feels a lot more intuitive.
I can look up say a Fastify, Golang, even some F# examples with something like Giraffe and get a web app quickly with little code and no convention magic with everything being explicit yet concise. No fancy attributes I need to remember, no conventions in my startup classes I need to understand if any customisation, even dep injection is something to learn from people coming from some other platforms rather than just standard library calls composed together.
I actually like the .NET platform, don't get me wrong, I do think it is one of the best web platforms right now. Its more intuitive than Spring/Java as a comparison but that isn't saying much IMO. For newcomers I think the user experience to newcomers who are used to a lot less ceremony could be improved further. My first suggestion would be less "developer headspace required" to get started. On a personal note I think natural F# code tends to favor this from the teams I've been in but can't put my exact finger on a reason as to why - it feels more like coding Go/JS to me at least than C#.
As per another comment after having seen JS devs switch to F# for very large professional application and not wanting to go back I feel its fine for web. YMMV.
My observation is that the ex JS/Ruby/etc devs learning curve's seem to favor F# over C# as it is more like these languages. There's just a lot less to learn, and the coding style (e.g. modules, function first, etc) is more synonymous with langs like JS than heavy OO languages. I've recently inherited a team that moved to .NET from JS, and getting them to try C# has been painful especially when using things like ASP.NET. There's a lot of knowledge I just took for granted - we don't realise how much knowledge is required to use standard Java/C# OO in a production like setting that many dev's stumbled on when they were junior but have long forgotten the learning curve they went through. They tend to be framework heavy/dependent and require much more experience. From patterns (what's a strategy, repository, etc etc), to dep injection frameworks, to which refactoring tools I need, etc, etc - where in F# in my recent experience the experience is more, but still not quite "just code and work it out as you go".
The pit of success favors F# over C# in my experience with these teams especially if you keep it simple with the features used.
My experience has been that F# appeals to coders who are using the .NET platform coming from languages like JS/Go/etc but also has static typing who usually at least in the jobs I've been in are often on a Mac. C# dev's typically come from Java/C# enterprise kind of shops. Not one is better than the other and I will work in whatever space has interesting problems to solve - I think the difference is more cultural than technical.
What I've always wanted in Postgres in one quote expect to also allow other index types (e.g. Hash/BRIN/etc) in its language.
Side benefit: It also allows dev's to understand the pro's/con's of these constructs when using each one and build up their knowledge on what is really just algo's and data structures.
For example (bank money = real money) only because the bank is willing to keep the peg at (1.00 bank credit = 1.00 real currency) as you state whenever you use an ATM, take money out at the counter, etc. When the bank runs out of real money to maintain this peg it can and has deviated in the past (e.g. people selling their bank accounts at say 30c to the dollar in the great depression). In normal times however their much smaller money stock is enough to keep the peg going against the usual net deposit/withdrawal flow.
Of course if a central bank comes in and can lend that bank unlimited real money the peg could be maintained. Indeed that can and has happened.
C# Fluent API's aren't really piped things since they usually just mutate a variable underneath (hence the ignore often at the end of them in F#). I don't think fluent API's and piped functions are the same thing, and the pipe operator allows any function that matches the signature to be put in.
To be honest I think each language should play to their strengths and only "steal" things that benefit their current market of users and how they work. If it augments them that's fine. Otherwise you risk cluttering the language with too many ways to do things. If you want all these F# features you probably just want F# - its not too hard to call `dotnet new classlib -lang F#` these days and mix both in together in a solution.
Having worked in a few C++ shops, at least from my experience, it can result in a lot of useless debates and internal wars if you keep on adding different features from different paradigms into the language whilst trying to keep backwards compatibility.
My view is that if you want unions/non-nullability/data vs classes even if C# gives you the features, it feels more natural to code that way in F# due to the environment around the features and years of optimising that code path. It's just less noisy and less workaround'y. i.e. each language typically prioritizes enhancements and features based on what users in that language are having pain points on. In that sense F# is much further on the data/function paradigm than C# is - conversely when doing low level stuff C# is often in front having to cater for that for some time.
Type inference isn't really in C# other than some simple parts, nor are some of the features that sometimes give F# an advantage with performance that I've used. When you get used to it I find I have to transpile some problems to C# how I would write them in F# with duplication to get the same performance depending on the problem.
If you are used to C# that's fine. Using both I still think F# is usually the better language these days if your coming into it from scratch, although if you aren't maybe the "sunk cost" isn't worth it?
On a side note the performance thing seems to be a myth to me. There's no reason C# or F# should be faster or slower than one another with enough effort given the same underlying runtime. Many of the functional features are moving to be lower/zero cost in F# (inline lambdas, state machines, etc). They all compile down to IL in the end so as long as you can get the IL you want does it matter? Sure it may be easier to express more performant code in one or another depending on the domain. I could argue many cases where F# was easier to write performant code (numeric, generic algo's, explicit inline functions, inline vs ref, tail rec, compile time polymorphism, avoiding virtual dispatch preferring static functions, etc) and vice versa (native interop, until recently specialised async/await, etc).
For better or worse all that means is that viruses may evolve to delay their symptoms rather than overall peak severity if that's possible. It means still just as delay but with a good enough delay to spread.
I don't really see the selective pressure however for this virus to become more benign. It seems to have enough people to infect without it given how contagious it is. It doesn't really have to become more benign to allow even more spread.
Having written persistent data structures in other lang's that are quite capable (170ns lookup 10,000,000 elements approx to give a rough guide) - yes it is an order slower than unordered hash mutable structures but often still faster than for example the mutable ordered ones I've tested with the advantage of still being immutable. This does benefit some scenarios where you want data sharing/cheap clones of data.
They avoided COVID delta for months (6+) despite locking all incoming arrivals in shared air hotel quarantine systems and workers coming into the quarantine zone day in and day out (i.e. not taking quarantine seriously quarantine). Some Australian states who had better border control policies were able to keep COVID out indefinitely (e.g. WA, QLD, TAS, SA, etc).
Knowing some people who live in WA, despite overseas travel restrictions, life is pretty much pre-2019. Clubs are open, schools are running, no masks, nothing. They are thankful for zero COVID policies - the biggest one being border control and for some areas dedicated, but well resourced, quarantine facilities (NT). Only recently did NT get cases, and it didn't come internationally, but from the NSW outbreak. It's a concern since the indigenous population there typically have less health facilities.
Of course it is much easier to do this in Australia than the US or UK for a variety of reasons. Europe definitely can't do this IMO - land borders aren't strong enough.
When I hear words like everything, everyone, everywhere to describe any system I get disheartened failure is almost assured. I wouldn't design software like that for example. Whether they are engineering systems, social systems, or in this case viruses. Relying on every part to comply is the opposite of redundancy. The probability of getting everyone to comply in a group as large as this planet all at once, at the same time without lags (e.g. vaccine rollouts), etc is sadly IMO extremely small.
We probably need new approaches, some which may challenge the current status quo. I remember watching a video early in COVID where isolating populations into smaller groups allows quick treatment of each group and the ability to contain the spread (i.e. limiting the R factor). As long as highly available world air travel is around, reducing redundancy and increasing the interconnectness of our populations into a single system the virus will have an advantage IMO.