jonnifyfsharp
C0.2.1

Managed execution

The CLR

Two languages, one intermediate language, one machine to run it.

The Common Language Runtime is .NET's virtual machine. F# source does not run directly; it is compiled to a portable intermediate language packaged in an assembly, and the runtime turns that into native code at the moment it is needed. The same machine runs C#. This dive follows a tiny function down through that pipeline, then names the two ideas that make the shared machine possible: a common type system, and the metadata that lets one assembly call another.

From source to native: the managed pipeline

"Managed" means the runtime, not the program, owns the low-level work — memory, type safety, and the final translation to machine code. The path from text to execution has a fixed shape. A compiler reads F# (or C#) source and emits CIL — Common Intermediate Language, once called MSIL — a stack-based instruction set that no CPU runs directly. The CIL, plus type metadata, is packaged in an assembly: a .dll or .exe file. At run time the JIT compiler translates each method's CIL into native instructions the first time it is called, and the garbage collector manages the heap underneath.

Step the example through the stages below. The source is a one-line F# function that adds two integers; the same function written in C# compiles to the same CIL, because both target the one runtime.

The managed execution pipeline · step through it

stage 1 / 5
source F# / C# compile → CIL assembly .dll + metadata JIT → native run GC manages the heap
stage 1 · source — An F# function let add x y = x + y. Plain text, not yet runnable; the same logic in C# is int Add(int x, int y) => x + y;.

The CIL below uses four real, well-known opcodes for a trivial two-argument add: ldarg.0 and ldarg.1 push the two arguments onto the evaluation stack, add pops them and pushes the sum, and ret returns the value on top. The exact bytes a real compiler emits include extra metadata; this is the shape, not a literal transcript.

// the body of `add`, in CIL — four opcodes
ldarg.0   // push argument 0 (x) onto the stack
ldarg.1   // push argument 1 (y) onto the stack
add       // pop x and y, push x + y
ret       // return the value on top of the stack

The compiler's job ends at CIL. From there the runtime takes over — JIT to native, run, collect. That hand-off is what "managed" names.

One type system: the CTS

The shared machine needs a shared notion of "type". That is the Common Type System (CTS): the rules for what a type is, how values are laid out, and how methods are called, defined once for the whole platform. An F# record, an F# tuple, an F# function value — each is a .NET type under the CTS, the same kind of thing a C# class or struct is. Because there is one type system, an F# value crossing into C# code is not translated or wrapped; it is already a .NET object.

A narrower contract sits inside the CTS: the Common Language Specification (CLS), the subset of features every .NET language is expected to understand. A library that stays within the CLS can be consumed from any .NET language. This is the mechanism behind the module's headline — F# can call a C# library, and C# can call an F# library, because both speak the CTS.

The idea

One type system for the whole platform. A value has one .NET type, whatever language declared it.

In F#

An F# record is a CTS reference type; an int is System.Int32. C# code receives them as ordinary .NET objects.

"F# is a .NET language" is not a slogan. It is a statement about the type system: F#'s values are the platform's types.

The standard, and real generics

The runtime, the intermediate language, and the type system are not Microsoft-internal details. They are standardised as the Common Language Infrastructure (CLI), published as ECMA-335 (also ISO/IEC 23271). ECMA-335 defines CIL, the CTS, the metadata format, and the virtual execution system — the contract the CLR implements. (C# the language has its own standard, ECMA-334.)

One feature of that type system matters especially for F#: generics. The CLR gained real generic types in .NET 2.0 (2005), with runtime support — generic instantiations are reified, carrying their type arguments at run time, not erased. That work came from Microsoft Research, by Don Syme and Andrew Kennedy. F#'s parametric polymorphism maps onto these real CLR generics, which is one reason F#'s functional style runs without a translation penalty: a generic F# function is a generic .NET method.

CLR
Common Language Runtime — the virtual machine that JIT-compiles CIL and runs it under garbage collection.
CIL
Common Intermediate Language (once MSIL) — the stack-based instruction set every .NET compiler emits.
assembly
A .dll / .exe packaging CIL plus type metadata; the unit of deployment.
CTS / CLS
Common Type System and its cross-language subset — the shared type vocabulary that makes interop possible.
ECMA-335
The Common Language Infrastructure standard (also ISO/IEC 23271): CIL, the CTS, metadata, the execution model.
generics
Reified runtime generics, added in .NET 2.0 (2005) from MSR work by Don Syme and Andrew Kennedy.

The CLR is a published standard with reified generics — which is why F#'s polymorphic functions compile to ordinary, generic .NET methods.

References

Sources

Related in this course