What is E#
E# is a general-purpose programming language for the .NET Common Language Runtime (CLR). Source
files end in .es, and they compile through Mono.Cecil straight to ordinary .NET assemblies. A type
written in E# is a real CLR type: a C# project references the .dll and uses it without knowing or
caring that it wasn’t C#.
It is not ECMAScript (the .es extension is a collision, nothing more), and it is not a
preprocessor or a dialect of C#. It is its own language that shares C#‘s runtime.
Why it exists
Section titled “Why it exists”E# started from a wish list, not a gap in the market. The type system should read like Go — small,
regular, few surprises — but go further than Go does: real generics, classes when a problem wants
them, and a good chunk of the ideas Rust gets right in its type system (tagged unions you match
exhaustively, errors as values, the -> return syntax). Concurrency modelled on what Go and Swift do
well. And an object model that lands between Go and C# — more structure than Go, far less ceremony
than C#. That combination didn’t exist on the CLR, so E# is it.
Targeting the CLR was a goal from day one, and that’s where the second pillar comes in. Because E# is a first-class CLR citizen, it can do something .NET never really had. The JVM has long been polyglot in the small: Java, Scala, and Kotlin mix inside one project, not just across a package boundary. The CLR never got that — you had C#, and while F# exists, it doesn’t drop into a C# codebase the way Scala drops into a Java one.
E# closes that case. .es and .cs files compile into a single assembly — drop both kinds in a
directory and point the compiler at it: the E# half emits IL through Mono.Cecil, Roslyn compiles the
C# half, and ILRepack fuses both into one .dll with bidirectional references. (Pure-E# code skips
Roslyn entirely.) No NuGet boundary between them, no interop attributes, no two-projects-and-a-reference
dance. This was an early priority precisely because it’s the payoff of being native to the runtime
rather than bolted on after — a big part of the language, even if it isn’t the whole reason for it.
The shape of it
Section titled “The shape of it”E# borrows the readability of modern languages and puts it on a runtime many of us already use:
->return arrows and name-before-type annotations (func add(a: int, b: int) -> int)- expression-bodied functions (
func double(x: int) -> int = x * 2) - value types by default, with the object world available when you ask for it
- errors as values;
matchover tagged unions; uncolored-by-default async (semi-colored when you want the machinery)
The goal is not to out-feature C#. C# is, by feature count, about the largest language going (before you even reach its BCL). E# goes the other way: a smaller, composable core. Modern, object-oriented, and — relative to C# — simple.
namespace Geometry
data Point { x: int, y: int }
// A free function whose first parameter is `Point` becomes a method on it —// composition by attachment, instead of methods trapped inside a class body.func dist2(p: Point) -> int = p.x * p.x + p.y * p.y
func demo() -> int { let p = Point { x: 3, y: 4 } return p.dist2() // 25 — called as a method}Object-oriented, with a procedural front door
Section titled “Object-oriented, with a procedural front door”There is no class keyword. data (a value type) and ref data (a class, with identity) cover
the type story; static func gives you a static class. Methods attach to a type through
instance-method promotion rather than living inside it, and composition leans on interface
conformance plus that promotion. Inheritance exists on ref data but is opt-in and sealed by
default — the opposite of C#‘s “inheritable unless you say otherwise.”
If you want a one-line placement: E#‘s object-orientation sits between Go and C# — more than Go (which has no classes or inheritance), less than C# (where everything is a class).
That choice extends to how a program starts. An executable is entered through main, in either
shape: a bare top-level func main() (the program is a function), or a main method on a
ref data Program (the program is an object). The class-style needs no launcher — the compiler
enters it as Program().main():
// function-style // class-stylefunc main() -> int { ref data Program { return run() func main() -> int { return run() }} }Where it’s a good fit
Section titled “Where it’s a good fit”E# is general-purpose — it covers the same range as any other CLR language, with no single intended niche. Where it slots in is shaped by mechanics rather than preference:
- In an existing .NET project —
.esand.cscompile into one assembly, so E# can be adopted incrementally, a file at a time, instead of as a separate project. - As a library — an E# type is an ordinary CLR type; C# and F# consume it directly, with no shim.
- Standalone — pure-E# code compiles straight to an assembly through Mono.Cecil.
Status
Section titled “Status”E# is pre-alpha. The language is real and tested — the IL compiler is the source of truth, and every assembly the test harness produces is run through ILVerify — but the surface is still moving and some corners are unfinished.
The most honest picture of what compiles today is the corpus: a large set of real
.es programs drawn from the language’s own test suite and samples — passing test cases, showcases,
and integration tests — each one compiled cleanly (or rejected with its expected diagnostic) and either
producing its asserted output or standing as a real, well-formed program. Start there, or walk the
guide:
- Types —
data,ref data,choice,enum,interface - Functions & promotion — free functions, methods by attachment,
static func - Control flow & match —
if, loops, and pattern matching - Errors: Result & ? — errors as values
- Pointers & memory —
*T,new,&, and the mutation triad - Async & concurrency — uncolored async, channels, structured concurrency
- Interop & delegates — the polyglot payoff
- Showcase — curated, heavily-commented programs end to end