jonnifyfsharp

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# habit what you reach for idiomatic F# what F# would write null → Option

C# — the habit

string FindUser(int id)
{
    return null; // "not found"
}

F# — the idiom

let findUser id : string option =
    None   // "not found"
Absence is a value — string option, not a null reference. The caller must open it with a match, so the unhandled-null bug cannot compile.

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

built lesson ready soon specified
C2.01built

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
C2.02built

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
C2.03built

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
C2.04built

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
C2.05built

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
C2.06built

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
C2.07built

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
C2.08built

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
C2.09built

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

Related in this course