Native to the CLR
Compiles through Mono.Cecil to ordinary IL. E# types are indistinguishable from C# types to
a consumer — reference the .dll and call them. Interop →
v0.1 · pre-alpha The language and compiler is currently pre-alpha. The compiler is functional for its test suite, and a small but growing set of real programs. I would not utilize the lnaguage in production yet — many small features are still awaiting implementation, and the compiler is still being hardened for robustness and performance.
The E# programming language is a general-purpose language that compiles to standard .NET
assemblies, the same Common Language Runtime (CLR) that runs C#, F#, and Visual Basic. A .es file becomes
a standard assembly .dll that any C# .NET project can reference without knowing it wasn’t C#. The file extension is .es and the build file is .esproj, though this may change in the future due to conflicting with the ECMAScript (JavaScript) build system in dotnet.
E# it’s its own language that utilizes the Common
Language Infrastructure (CLI) of the Common Language Runtime (CLR) to produce standard .NET assemblies that can be run anywhere the .NET runtime is available. The language is designed to be readable, maintain a smaller surface area than C#, and be enjoyable to work with.
In no order, the language draws inspiration from Swift, Go, Rust, C#, and Template Haskell. E# is a C-family language, procedular and object-oriented at its core, blending elements of functional programming where they fit.
namespace Demo
// `struct` is a value type: it copies on assignment, is compared by its fields,// and has no implicit heap or identity.struct Money { amount: int, currency: string }
// Utilize reciever-block form to attach `describe()` to money as an instance method// Note: `describe()` will also be in the method set of `*Moneyfunc (m: Money) describe() -> string = "{m.amount} {m.currency}"
// A `union` is a tagged union — the set of things that can happen, as data.union ParseError { empty badNumber(text: string)}
// Errors as values as a language / compiler feature.func parse(s: string) -> Result<Money, ParseError> { if s.Length == 0 { return error(.empty) } var n = 0 if !int.TryParse(s, out n) { return error(.badNumber(s)) } // BCL interop is direct return ok(Money { amount: n, currency: "USD" })}The premise is simple: the language you write in shapes how you think about the problem, and the more surface a language carries, the further the code drifts from the model it is meant to hold. E# keeps that surface small — what you write stays close to the shape of the problem, so a program fits in your head, reads back a month later without reverse-engineering the ceremony, and says the same thing in less code because there is usually one obvious way to say it. The better practices are meant to fall out of the grammar, not a style guide.
Concretely, it came from wanting a language the CLR didn’t have: a type system that reads like
Go but goes further — generics, classes when you need them, and ideas borrowed from Rust (tagged
unions you match exhaustively, errors as values, the -> return syntax). Concurrency shaped by
Go and Swift. An object model that sits between Go and C#: more structure than Go, less ceremony
than C#.
Being a first-class CLR citizen also opens a direction .NET never really had: compiling .es and
.cs together into a single assembly — the JVM’s in-project polyglot story, on the CLR. This is one
feature of the compiler workspace, not the primary way to use the language; E# stands on its own and
emits ordinary assemblies. We aim for the full JVM level of support and robustness here and are still
exploring it in depth — the mechanism is a staged joint compilation, described
below.
The aim isn’t more features than C#; it’s fewer, composed well: value types by default, errors as values, uncolored async, methods that attach to types instead of being trapped inside them. The surface stays small less by leaving things out than by rarely offering the same thing twice — two ways to do something are usually two slightly different things. And it stays a C-family language throughout: imperative and object-oriented, with the functional tools at hand but not at the wheel — closer in temperament to C# than to F#.
Native to the CLR
Compiles through Mono.Cecil to ordinary IL. E# types are indistinguishable from C# types to
a consumer — reference the .dll and call them. Interop →
Value-first
struct is a value type and class is a reference type. Each keyword names the CLR form it
emits. Types →
Errors as values
Result<T, E> and ? propagation for in-language flow; try/catch only at the BCL
boundary. Errors →
Uncolored async
Any await promotes its function — no async keyword. Uncolored by default; semi-colored when
you want it, since the return type picks the machinery. Concurrency →
E# is general-purpose — it covers the same ground as any other CLR language, with no single
intended niche. Its primary shape is standalone. Mixing .es and .cs in one project is a further
feature of the workspace — one we are taking toward full JVM-level robustness and still actively
exploring — rather than the intended primary path.
The corpus is the most honest picture of what compiles today: 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.
A mixed .esproj is one project the E# compiler workspace drives end to end, in stages — the same
shape the JVM uses to compile Kotlin and Java together, adapted to the CLR. Each language is compiled
against the other’s declarations first and its compiled output second:
.es sources itself and hands the .cs sources to Roslyn..cs, resolving its types, base
classes, interfaces, and member signatures. The workspace projects those declared types into E#‘s own
symbol table as language-neutral handles — so E# code can name, call, extend, and derive from a C# type..cs, now resolving the E# types it uses.One workspace orchestrating two compilers and joining their outputs — no fork of Roslyn or the E# compiler, just a staged pipeline. This is the layer we are still hardening toward JVM-level completeness, and where the rough edges live today. See a worked example — a C# base, an E# type extending it, and a C# type extending that, in one project.
Some of this leans on Roslyn behaviors it was not specifically designed to provide — the same kind of unsanctioned platform use early Kotlin made of the JVM — which is part of why this layer is experimental rather than guaranteed.