F# Internals, History and Best Practices
52.1 How F# Compiles
FSharp.Core is the core library (List, Map, Option, Async, printf, and more). The compiler itself (FCS, F# Compiler Services) powers tooling like Ionide, Fantomas, and Fable.
Source → Lexer/Parser (indentation-aware) → Type checking & inference (Hindley–Milner + .NET OO) → Optimization
→ IL (Common Intermediate Language) → .NET runtime (JIT/ReadyToRun/Native AOT)
Unions → class hierarchies (or structs); records → sealed classes with structural equality; modules → static classes;
closures → FSharpFunc objects; curried functions → optimized direct calls when fully applied; seq/task → state machines52.2 History
Microsoft Research release (OCaml-inspired) Ships in Visual Studio 2010; units of measure, async workflows Type providers, query expressions
Constructors as functions, simplified mutable/ref, open source and cross-platform focus
Struct tuples/records/unions, nameof (4.7 preview), implicit yields, .NET Core support
#r "nuget:" in scripts, string interpolation, nameof, open type declarations
task { } builder (resumable code), expr[idx] indexing, InlineIfLambda
Static abstract members in interfaces (IWSAM), simplified SRTP
_.Property lambda shorthand, extended string interpolation ($$"""), [<TailCall>], nested record copy-and-update
Nullable reference types, .Is* properties on unions, partial active patterns returning bool, empty-bodied CEs
Ships with .NET 10 (check the release notes for details)
52.3 Best Practices
✅ Prefer immutable data, pure functions, and expressions. Keep side effects at the edges.
✅ Model the domain with records, unions, and single-case wrappers. Make illegal states unrepresentable.
✅ Use Option and Result instead of null and exceptions for expected failures.
✅ Treat incomplete-pattern warnings as errors (<TreatWarningsAsErrors>/<WarnOn>).
✅ Use pipelines for readable data flow, and modules to organize functions around types.
✅ Use task { } for ASP.NET/.NET interop and async { } when you need cold, cancellable workflows.
✅ Keep files ordered from domain → logic → infrastructure → entry point.
✅ Format with Fantomas, lint with analyzers, and test with Expecto/xUnit + FsCheck.
✅ Learn from *Domain Modeling Made Functional* and the F# style guide on docs.microsoft.com.
❌ Overusing classes and inheritance where records, unions, and functions are clearer
❌ null in F# APIs (use Option; enable nullness checking at .NET boundaries)
❌ Partial functions like List.head on possibly empty lists (use List.tryHead)
❌ Excessive custom operators and point-free code that hurts readability
❌ Blocking on async code (.Result, RunSynchronously) inside server request paths
❌ Mutable state shared across threads without agents, locks, or immutable snapshots