I dared to ask two questions on twitter:
This post summarizes the answers and my own opinion on where C# and F# shine.
What is better in C#
These are the things that were mentioned that make C# superior:
Task Runtime Performance
Asynchronous code (Tasks) in C# runs faster than in F# because the compiler supports them natively and generates optimized code. The difference may be reduced, once F# supports tasks natively, too.
While this is true, I wonder how relevant the difference is for a typical line of business application. For really performance-sensitive code, I’d extract that into a C# library (or something even speedier) and call it from F#.
We use mainly async-workflow in our code because they are easier to use for us than tasks because they are cold, not hot and support cancellation out-of-the-box.
Interacting with .Net libraries
Most .Net libraries are written in C# and therefore they sometimes are easier to use from C# than F#.
In C# a method can return by just calling
return. In F# that is not possible.
This is helpful in deeply nested logic blocks.
Early returns are a normal thing in C#, however, they are actually bad design because they make understanding code often very hard. Some insight here (point 3).
C# supports co/contravariance, F# does not (yet?). This is especially helpful for library code that deals with generics.
Protected, Partial Classes, Inner Class Types, Implicit Interface Implementations, OOP in general
C# has the
protected keyword, F# does not. C# knows partial classes, inner class types, and implicit interface implementations, F# does not.
Generally, programming in an object-oriented way goes easier in C#.
C# supports implicit casts, F# does not. Neither up, nor downcasts. C# makes it therefore easier to use libraries that rely on implicit casts a lot.
No implicit casts reduces defects.
Source Generators are unknown to F#.
In C#, files and namespaces can be ordered in any way. F# has a strict order (bottom knows top).
Therefore it is a bit easier to define models in C# that need cycles. In F#, you have to use the
and keyword and put the types/functions side-by-side.
Tooling and IDE Support
C# has the better tooling and IDE support than F#.
The debugging experience is better in C# across all IDEs than in F#. Especially async workflows are sometimes hard to debug.
This was the most mentioned thing that is better in C# than F#.
Things like P/Invoke and unsafe code belong into C#. C# also supports pinned ref and Span.
C# is made for programming WinForms or WPF clients. That is definitely not the main area of F#.
Finding examples and resources
It is easier to find examples, solutions and resources for C# in the internet.
Of course, there is much more C# content available in the internet. However, when it comes to quality content, I’m not so sure anymore whether F# really lacks behind C#. The F# community is also very helpful – as you can see by the number of responses I got on these tweets.
Entity Framework is a very popular framework in the .Net world. General consensus is that you should not try to use it from F#. Its design goes directly against F# opinions on immutability and functions-first, OO-second.
I would never use EF in C# either. Fully-fledged ORMs like EF have some major drawbacks that I wouldn’t want in my code. I once did a talk on this topic, but there is no video. So the next best thing is to read this.
Async Main Method
In C# you can put
async on the
main method. In F#, you have to use
Async.RunSynchronously in the
main method instead.
What is better in F#?
These were the things that were mentions that make F# superior:
Immutability by Default
In F#, unless you explicitly use the
mutable keyword, everything is immutable. Immutability helps prevent defects and makes parallelization easier.
Pipes in F# allow to write code top-left to bottom-right without using local variables. This makes code easier to read.
Everything is an Expression
Expressions make it easier to reason about code and bugs have a more difficult time to hide. In C#, there are expressions and statements, in F# only expressions.
F# type inference makes you type less type annotations and makes refactoring easier.
F# knows discriminated unions and therefore allows to model a business domain much better than when only using classes, interfaces, enums, and records. Better modelling leads to less defects and simpler code.
Strict Order of Files
F# has a strict order on the files and the files’ content. Only what is declared above can be used. This prevents cycles by design. Cycles are one of the hardest problems in software design because they make refactoring very hard. You need no additional tool to prevent against such cycles because F# forces you to define no cycles (except for type and function definitions using
F# knows computation expressions, which allow combining different aspects (async, Option, Result, Validation) in a programmer-friendly way. FsToolkit is a great library providing many CEs. In C#, combining
async/await with anything results in hard to read and/or bloated code.
C# has lately improved a lot regarding pattern matching. However, F# has still an advantage. F# also knows Active Pattern that can make complicated matching patterns easy to read.
F# know measures, C# does not. Measures make code more expressive and less bug-prone in calculations.
Easier to Merge
In C#, many merge conflicts are caused be
) at the end of a method declaration. F# does not have this problem.
F# is the simpler Language
F# is built for simplicity. F# currently knows 71 keywords, C# has above 110.
F# has only one way to define a record, C# has 4.
While tooling in C# is generally better, there is no auto-syntax-layouting tool in C#. In F# there is Fantomas. Readable code without StyleCop-build-warnings.
Partial Application, Composability
F# provides partial application, C# does not. Partial application allows for piping and creating better composable designs.
Both C# and F# are great languages. However, both have unique strengths. So I suggest that you look at your situation, what kind of application or system you are building and decide which language better suites your needs.
Also, keep in mind that you can mix C# and F# in the same solution. Interoperability is great between these both languages. Consuming C# from F# is however to be preferred because F# knows most of C#’s concepts and constructs, C# does not know about F# specifics.