7 Reasons F# Sucks
Date: 2025-06-18 | build | create | csharp | fsharp | tech |
I came across Onur Gumus' Top 7 Reasons F# Sucks on Linkedin (and Reddit) and thought it was worth a reblog. I frequently write about F# here and sometimes it's nice to get another perspective.
So here are the top 7 reasons that F# sucks and some personal anecdotes from my years working with F# to go with them.
7. You start talking weird
“You say “computation expressions” and “railway-oriented programming” out loud, and suddenly your team stops inviting you to lunch.”
F#'s syntax, ML background, and type system lends itself towards patterns that are not as common in more mainstream c-like languages. This means a whole world of patterns that you or your coworkers may not have seen yet - or may have seen just by a different name.
- Computation expressions
- Railway-oriented programming
- Partial application
- Currying
- Monads
- Actors
- Pipes
- Discriminated Unions
- Exhaustive pattern matching
- Option / Result chaining
I've found that even promoting Result or Option types has rendered weird looks from coworkers, especially if they haven't used a "niche" language themselves a la Rust, Haskell, Scala, etc that uses these (Here's my attempt to promote Results in Python).
That said, I think this oddness is a good thing. It means you're being exposed to "new" ideas that are p good actually. At the very least it forces you to consider a different way to program and while you might not take everything with you, there's sure to be some paradigms you find useful.
6. Nulls haunt you
“You used to live with null. Now when you see one, your eye starts to flinch, like a war flashback.”
F# does a pretty good job of removing nulls internally - though you still need to program defensively at the edges. F# prefers Options - a value either exists (Some) or is missing (None). Couple this with exhaustive pattern matching and we get a nice way to describe and ensure we handle these cases.
Nulls are the billion dollar mistake but F# forces you to handle them with exhaustive pattern matching which reduces the surface area of potential errors. Missing values are a common scenario so I don't think we can (or should) get rid of their representation entirely - but we should make the language help you spot and handle them safely.
Related: How to Write Simple, Clear F# Option Pipelines with Option.orElseWith
Note that Dotnet WILL try to leak nulls when interacting with some C# libs (cough EF cough) but this is relatively easy to handle when you're on the lookout for them.
5. Your buggy code won't even compile
“F# refuses to run until you’ve handled every weird edge case.”
F# supports expressive types, discriminated unions, and exhaustive pattern matching which helps you model your domain precisely and ensure you've handled all of its edge cases.
Now this won't catch everything but it does catch a large swath of issues where you said a type was A but your code is treating it like B or is failing to handle some case of A. So generally if it compiles, it's more likely to have less bugs cause at least you've covered all cases as described by the types.
You should still write tests of course but you'll find that strong expressive types can allow you to make many failure cases unrepresentable so the compiler protects you from them w no need for tests.
4. C# follows F# features from 10 years ago
“and you’ll painfully watch it catch up, one keynote at a time.”
It is always amusing to watch a company or technology proudly announce features that your choice has had for years and even decades. C# does make big releases each year and lately most have pulled a feature from F# - often in a less ergonomic form.
And, similar to Apple's keynotes pulling from Android, C#'s releases often get more hype. Amusing but not totally uncalled for - they are better at producing hype, the feature is already proven and asked for by the community, and (at least in C#'s case) has a much larger userbase to hype up.
Honestly - I'm not mad about it. I'm glad to see C# pulling from some of the great design features F# has and figuring out a way to make it work for its existing codebase and audience.
It's a sign that the creators care about the language and their userbase and are willing to learn from other languages to improve their own. It gives me confidence that 5 years from now C# will be a much better language than it is today and a little hope that other mainstream languages will continue this trend - bringing more powerful and ergonomic tools to their ecosystems.
C# now has:
- Immutable records
- Pattern matching (a bit clunky but they exist!)
- Discriminated Unions (though a bit clunky)
- Pipes (via an external package)
- Null safety (if you turn it on in project)
So it took awhile and they're maybe not as elegant as F#'s implementations but they do exist which makes C# a much better lang overall IMO.
3. The job market is a desert
“You’re not unemployable, you’re niche.”
The truth is the F# job market is small. Jobs do exist but they are rare. This is one of the biggest problems with the F# ecosystem today (see: The State of F#) and is somewhat of a programming language death (or at least stagnation) spiral.
F# jobs do exist but I wouldn't bet my career on one (and I personally have never had a gig that wrote F#). On the other hand, I do think the patterns and ideas you learn while coding with F# will uplevel your understanding of programming patterns and make you a better engineer and thus a more hirable engineer over time. But I realize that's kind of secondary and doesn't help you pay your bills today.
There's nothing stopping you from building a successful business with F# - it's quite good at domain modeling and general purpose programming. I've personally built many of my projects with F# and built a project template (CloudSeed) to help me launch F# webapps faster. And there are dozens of companies all over the world using F# in production.
But it's true - there just aren't that many jobs available.
Related: What we learned running F# in production for 5 years
2. Making illegal states unrepresentable becomes an obsession
“Three months later… nothing compiles, and you cry in union types.”
Building with types forced me into a different way of thinking. It forces you to precisely model your domain up front so that you can handle all its permutations later.
This is I think what sits at the core of functional programming - modeling data and transformations separately. Whereas OO tends to focus more on data and transformations together.
In general this is great because it does catch a lot of edge case behavior and make you handle them. But it can be a pain as you now have to handle those cases (whereas another lang might've missed them completely or decided to throw exceptions to avoid handling them entirely).
Add in a bit of eagerness and you might find yourself trying to define the whole universe which can be a tricky prospect.
For more, I'd recommend Domain Modeling made Functional by Scott Wlaschin (who also writes F# for fun and profit). It's the best book on DDD I've ever read and does a great job of connecting types to business domains and exploring how to build real systems from that including examples in F#.
1. You can't go back
“Once you’ve written F#, every other language feels like hand-writing in Wingdings font.”
F# is an amazingly designed language and there are bits you will miss in other langs:
- Pipes
- Expressive, strong types
- Unions and exhaustive pattern matching
- Collection iterations
- (some of) the syntax
But I will note that other languages are taking note and building their own versions of these things when they make sense. They might not be as ergonomic and they might not even be in the standard library but most of them can be done effectively in most languages.
So even if you miss some of these things - don't fret too much. Maybe it just needs a bit of activism / open source contributions to get it in your language of choice.
In my experience, you can get 60-80% of the good parts in most languages, even if they are ~2x as clunky at first.
As an example, I think F# beats out Python in many cases but you can still bring F#-like records and Result types in Python, they're just not in the standard library.
Next
F# remains an A Tier language for me but, like any technology choice, it does have its flaws. Jobs are scarce and the community is small but I still recommend people try it because it's fun() and you'll learn some interesting things along the way.
If you're curious about building webapps with F#, I built CloudSeed - an F# project boilerplate - to make spinning up F# webapps easier. More: Spin up a Fullstack F# WebApp in 10 minutes with the CloudSeed Project Template.
If you liked this post you might also like:
Want more like this?
The best way to support my work is to like / comment / share for the algorithm and subscribe for future updates.