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 machines

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