Chapter C2
F# for C# developers
You already know .NET. This chapter ports the habits, not the runtime.
A working C# team has been handed an F# project. Nine modules translate every C# reflex into its idiomatic F# form so the team is productive on day one: classes become modules and functions, mutable fields become immutable values and expressions, null and exceptions become Option and Result, inheritance becomes discriminated unions, loops become recursion and fold, LINQ becomes seq pipelines — and the two languages meet cleanly across one solution. Every C# and F# snippet is real and runnable.
How to read this chapter
F# runs on the same .NET runtime as C#: the same types, the same libraries, the same garbage
collector. What changes is the defaults. Where C# makes you opt in to
immutability, F# makes you opt out. Where C# uses null for "missing" and
exceptions for failure, F# puts both in the type, so the compiler forces you to handle them.
So this is not a language you learn from zero — it is a set of translations. Each module takes one C# habit and shows its idiomatic F# form, with the C# you know beside the F# you are learning. Select a habit below to see the pair.
C# habit · its idiomatic F# form
C# — the habit
string FindUser(int id) { return null; // "not found" }
F# — the idiom
let findUser id : string option = None // "not found"
F# is not a new platform — it is new defaults on the platform you have. Learn the translations and the rest is the .NET you already know.
The nine modules
The chapter in order, the final module a hands-on lab. The arc runs from the object habits — classes, mutation, null and exceptions — through the functional core — unions, recursion, functions, sequences — to interop and a mixing-paradigms lab. Each module carries three dives, C# beside F#.
From classes to modules & functions
Where behaviour lives when there is no class to hang it on.
- C2.01.1Static classes → modules
- C2.01.2Instance methods → functions
- C2.01.3Namespaces, members & file order
From mutable state to immutability & expressions
Everything is an expression; values do not change.
- C2.02.1Expressions, not statements
- C2.02.2Immutable by default
- C2.02.3Mutation, contained
From null to Option, from exceptions to Result
Absence and failure become ordinary, type-checked values.
- C2.03.1From null to Option
- C2.03.2From exceptions to Result
- C2.03.3The interop boundary
From inheritance to discriminated unions
Model variation with data + pattern matching, not hierarchies.
- C2.04.1Class hierarchy → DU
- C2.04.2Virtual dispatch → match
- C2.04.3The Visitor pattern → recursion
From loops to recursion, map & fold
Iterating without mutating an index.
- C2.05.1Loops → recursion
- C2.05.2The accumulator → fold
- C2.05.3Transform → map & filter
From interfaces to function types & object expressions
A function is the smallest interface; object expressions fill the gaps.
- C2.06.1Single-method interface → function
- C2.06.2Object expressions
- C2.06.3Delegates, Func & events
From LINQ to seq pipelines
The query you know, as a lazy F# sequence pipeline.
- C2.07.1LINQ operators → Seq
- C2.07.2Deferred execution
- C2.07.3Query expressions
Interop — calling C# from F# and back
One solution, two languages, clean boundaries.
- C2.08.1Calling C# / the BCL
- C2.08.2Exposing F# to C#
- C2.08.3The null boundary
Mixing paradigms in one solution
A lab: an F# core behind a C# host, end to end.
- C2.09.1The functional core
- C2.09.2The imperative shell
- C2.09.3Ship it (the lab)
References
Sources
- Microsoft — F# documentation — the language guide and the F#-for-C#-developers material.
- Microsoft — F# style guide — idiomatic F# conventions for a team adopting it.
- FSharp.Core API reference — Option, Result, List, the operators.
- Microsoft — .NET documentation — the shared runtime, the CLR, and language interop.
Related in this course
- F# In Depth — course home — the full C0–C5 map.
- C1 · F# Language — the F# this chapter translates C# into.
- Functional Programming in Elixir — the same shift, on the BEAM.