Familiar semantics
Values are copied. Classes and arrays share identity through references. A garbage collector manages heap memory automatically; managed byrefs provide explicit access to storage.
Read the type contracts →Preview 8 · Runtime + Raven
neoCLR explores changes to the .NET platform model: familiar value/reference semantics and CLI metadata, with generic Void, explicit outcomes and its own System library.
Explore the runtime, type system and library through Raven code. Compare the contracts with .NET, try the current tools, and give feedback on the APIs still being designed.
A value when there is one.
import System.Option.*
func FirstPositive(values: Iterable<int>) -> Option<int> {
let value = values.First(number => number > 0)?
return Some(value)
}
Find a positive number, or return None. Absence is part of the contract.
Explore the example →01 — FAMILIAR FOUNDATIONS
A software platform brings together a type system, an execution engine, a class library and developer tools. neoCLR builds on .NET’s managed-platform model and explores focused improvements across those layers.
Values are copied. Classes and arrays share identity through references. A garbage collector manages heap memory automatically; managed byrefs provide explicit access to storage.
Read the type contracts →Types, members, signatures and assembly references follow CLI concepts. Raven emits CLI metadata and IL; the current bridge imports a supported subset into neoCLR.
Read the format direction →Keep familiar behavior wherever the platform does not deliberately diverge. Generic Void, typed outcomes and invariant mutable arrays have explicit benefits and migration costs.
Explore the platform direction →02 — RUNTIME
The current interpreter and garbage collector make neoCLR executable today. Initial debugger support helps inspect it. A JIT is a future possibility, not a shipped execution mode.
The current runtime interprets supported instructions and enforces its execution contracts. Raven programs reach it through the CLI importer; neoIL exposes the lower-level instruction surface directly.
Read the execution architecture →A single-threaded, nonmoving mark-and-sweep collector reclaims unreachable managed objects, including cycles. Initial GC counters and collection events help inspect its behavior.
Explore the collector →The terminal debugger supports stepping, breakpoints and inspection of call stacks, locals, heap objects and GC statistics. Raven source debugging in VS Code remains separate future work.
Try the runtime debugger →Exploring next: a JIT that compiles IL to native code, and improvements to the collector’s diagnostics, memory use and pause behavior. Compare these with .NET’s JIT and generational GC while preserving the platform’s contracts. Read the open questions →
03 — TYPE SYSTEM
Void has one logical value and can be used as a type argument. That lets the same generic API represent a useful result or completion without a payload.
Func<T, void> expresses a callback with no useful result. neoCLR uses one Func family, including Func<Void> and Func<T, Void>, where .NET uses Action. Result<Void, E> similarly represents completion or a recoverable error.
The Raven sample spells the unit type System.Void. Ordinary void-returning calls still leave no result on the IL stack; the compiler bridges the unit-value contexts.
var shared = 7
let read: Func<int> = () => shared
let write: Func<int, System.Void> = value => {
shared = value
}
write(42)
WriteLine(read())
WriteLine(shared)Both callbacks capture the same variable. Calling write changes it to 42; read returns 42. A Void result makes the writing callback fit the same generic delegate family.
Explore Raven callbacks →.function Complete() -> System.Result<Void,String>
ldvoid
newobj instance System.Result.Ok<Void>::.ctor(Void)
newobj instance System.Result<Void,String>::.ctor(System.Result.Ok<Void>)
ret
.endVoid is valid as a generic unit type. Ordinary void returns still leave no result on the evaluation stack. Unlike .NET System.Void, it can fill the success slot in a generic Result.
PREVIEW 8 API
Preview 8 includes these APIs. Their contracts are still evolving, and we welcome feedback from real programs.
func ShowCurrentTime() {
let clock: Clock = SystemClock()
let instant = clock.Now
let local = instant.ToLocalDateTime()
WriteLine(local.Date.Year)
WriteLine(local.Time.Hour)
}Clock exposes Now as an Instant. Pass a Clock into application code to substitute a fixed clock in tests. SystemClock reads the host wall clock; ToLocalDateTime uses the system time zone for this first demo. Duration represents a signed tick count. Arithmetic, scheduling and configurable calendars remain future work.
Explore dates and clocks →let assembly = RuntimeContext.Current.ExecutingAssembly
Console.WriteLine(assembly.Name)
for reference in assembly.ReferencedAssemblies {
Console.WriteLine(reference.Name)
}RuntimeContext identifies the executing assembly and its direct references. Both typeof and Object.GetType return TypeInfo; all Introspection collections use Sequence. Follow a runnable walkthrough of discovery, tokens and sealed member matching. Dynamic loading, invocation and code emission remain future work.
Explore Introspection in depth →04 — RUNTIME CLASS LIBRARY
Option for absence, Result for recoverable failure, and unions for outcomes with distinct cases. Explicit UTF-8 operations and modernized collection, date/time and LINQ APIs bring these choices into everyday programs.
These APIs are still being designed and evolving. Try the examples and help shape their contracts.
import System.Option.*
func FirstPositive(values: Iterable<int>) -> Option<int> {
let value = values.First(number => number > 0)?
return Some(value)
}
A query with no positive value returns None; a match returns Some(value). The ? operator stops on absence. Unlike .NET First or FirstOrDefault, this API returns an explicit optional outcome.
func Normalize(value: int) -> Result<int, OverflowError> {
let amount = Math.Abs(value)?
WriteLine("Continued")
return Result<int, OverflowError>.Ok(amount)
}The ? operator continues with a value or propagates the error. The return type makes both possibilities visible. In the full sample, −42 produces 42; the minimum Int32 value propagates OverflowError. This changes the exception-based .NET Math.Abs contract.
func Extract(text: string, start: int, count: int) -> Result<string, Utf8SliceError> {
let part = text.SliceUtf8(start, count)?
WriteLine("Sliced")
return Result<string, Utf8SliceError>.Ok(part)
}
For “Aé😀Z”, byte range (1, 2) yields “é”. Starting at byte 2 splits its encoding and returns InvalidBoundary. Unlike .NET Substring’s UTF-16 offsets, these offsets explicitly count UTF-8 bytes. Slicing copies text and checks code-point boundaries, not grapheme clusters.
func Read(values: Sequence<int>) {
WriteLine(values.Count)
WriteLine(values[0])
WriteLine(values.ToList().Count)
}
func Replace(values: MutableSequence<int>, value: int) {
values[0] = value
}
func Append(values: List<int>) {
values.Add(11)
}
Read accepts arrays and lists through Sequence. Replace needs MutableSequence; Append needs a growable List. These roles parallel .NET read-only and mutable collection interfaces with a distinct replacement capability. A read-only view still observes changes made through another alias.
CheckDate(Date.Create(day: 29, year: 2024, month: 2))
CheckDate(Date.FromDayNumber(dayNumber: 0))
CheckDate(Date.Create(2023, 2, 29))
CheckDate(Date.FromDayNumber(-1))The sample’s CheckDate helper matches Result<Date, InvalidDateError>. February 29 is accepted for 2024 and rejected for 2023; a negative day number is also rejected. Modern .NET already separates DateOnly and TimeOnly—the difference here is the typed failure contract and the evolving API shape.
func OnlyPositive(values: Iterable<int>) -> Result<int, SingleError> {
let value = values.Single(number => number > 0)?
return .Ok(value)
}
The development API uses Filter and Map for lazy queries (Preview 8 uses Where and Select); ToList materializes them. First and Last return Option. Here, for [0, 42], OnlyPositive returns Ok(42). For [0, 7, 42], it returns an error because two values match. Unlike .NET Enumerable.Single’s exceptions, missing or multiple matches use the Result return path.
import System.Option.*
import System.Result.*
func PrintOptional(value: Option<int>) {
match value {
Some(let found) => WriteLine(found)
None => WriteLine("Absent")
}
}
func PrintSingle(value: Result<int, SingleError>) {
match value {
Ok(let found) => WriteLine(found)
Error(let error) => WriteLine(error.ToString())
}
}Raven supports several union construction forms: a qualified factory such as Result<string, Utf8SliceError>.Ok(part), imported case names such as Ok, Error, Some and None, and contextual shorthand such as .Ok(value) where the type can be inferred. Explicit case/carrier construction is also available. The examples favor the shorter forms.
Short feature pages show what works and where the design may go next. They describe the development state, not a frozen API.
The excerpts come from executable Raven samples. Read the API coverage and migration policy for current limits.
05 — EASY MIGRATION PATH
Familiar value/reference semantics, CLI metadata and assembly concepts reduce what you need to relearn. neoCLR aims for a practical source migration path, with deliberate API differences made explicit.
Adapt code where outcomes change: null/default checks become Option cases, recoverable exceptions become Result, and Action callbacks become Func with Void. Mutable array covariance also needs adaptation. The preview supports a bounded importer, not unchanged .NET applications.
Read the migration policy →06 — TOOLING
Raven is the first language targeting neoCLR: a compiler we can shape alongside the platform while preserving its normal .NET target. It makes the platform’s ideas available through source code and familiar development tools.
neoCLR itself and programs running on neoCLR do not depend on .NET. The surrounding build tools and Raven Language Server run on .NET; the VS Code extension uses that server for editor features.
Follow the Raven walkthrough →The matching experimental Raven extension provides completion and hover for the target library. Dedicated neoCLR tasks build and run saved projects.
Set up VS Code →Inspect execution with the terminal debugger. Raven source debugging through the normal VS Code toolbar remains future work.
07 — WHAT’S NEXT
Our next release aims to put the proposed API shapes in place as a demo and proof of concept: coherent namespaces, types, capabilities and signatures, with working examples for selected paths. Complete implementations of every feature are not required.
We are moving existing APIs into that structure and porting the runtime class library from handwritten neoIL to Raven. Each area will distinguish executable functionality, declarations available for compilation, and designs still under discussion.
Read the next release objective →Explore the proposal overview and open questions →
These designs retain familiar .NET ergonomics while changing specific contracts. They are proposals for the next POC; the published Preview 8 download does not contain these complete API families.
String represents Unicode text with UTF-8 storage. The development Char represents a grapheme cluster, while explicit APIs expose scalars and bytes. This aims for a familiar .NET-like experience with text-oriented defaults; it changes .NET Char semantics.
Status: Grapheme Char, String length and iteration, scalar access and strict UTF-8 conversion work. Normalization, efficient traversal and text positions remain open.
Text proposal →Separate Instant and Duration from civil dates, calendar periods and time zones. An injected Clock makes time-dependent code testable; zone mapping exposes ambiguous and skipped local times.
Status: Initial Clock, Instant and Duration APIs exist in development, with their contracts or value implementations authored in Raven. System.Time migration, zones and periods are planned.
Time proposal →Separate CultureId, resolved Culture and contextual CultureProvider. Explicit cultures support predictable formatting and testing; an ambient facade keeps ordinary calls convenient. Data versions and fallback still need contracts.
Status: Globalization APIs are proposed. Localization remains a separate future domain.
Globalization proposal →Task<T> represents asynchronous completion; Result<T, E> carries expected failures. Await yields the value, and ? propagates a Result error. Task<Void> covers completion without a payload.
Status: Task APIs and runtime suspension remain proposed. Cancellation and operation ownership need further design.
Async proposal →Propose one System.Introspection model of Info interfaces without I-prefixes or public Type/TypeInfo pairs. V1 is runtime-backed through System.Runtime.RuntimeContext. Reflection binds and executes against that context; sibling Emit constructs code without implicitly loading it.
Status: The development library now has eight sealed Info interfaces, direct TypeInfo acquisition, executing-assembly discovery, module-scoped tokens and Sequence results. Discovery covers retained loaded metadata. Dynamic loading belongs to future RuntimeContext work; invocation, emit and offline contexts remain deferred.
Introspection proposal →Resolve pure Path values through a FileSystem capability and obtain files and directories from that context. Host and memory implementations can serve the same consumer, with explicit authority and resource lifetime contracts.
Status: Bounded static file helpers exist. FileSystem and its context-bound objects are proposed.
Filesystem proposal →Compose reading, writing, seeking and sizing interfaces, with separate async capabilities. Keep buffering explicit and text decoding above byte I/O. More interface types buy clearer requirements for consumers and decorators.
Status: Stream interfaces, buffer lifetimes and async I/O remain proposed.
Stream proposal →08 — TRY IT & HELP SHAPE IT
Preview 8 is available with a macOS arm64 runtime bundle, neoIL and Raven samples, and the matching experimental Raven SDK and VS Code extension. Standalone MSBuild builds Raven projects, including a small application-plus-library example. Source validation passes on Linux, macOS and Windows.
Try Preview 8 →09 — AREAS WE’RE EXPLORING
Further questions include nullability, memory views, callables, async, text, collections, clocks, JIT and GC. These are investigations rather than preview features; concrete use cases help guide the choices.
Could nullable and non-nullable uses become enforceable runtime contracts, beyond C#’s reference annotations? Construction, defaults, generics and compiler compatibility still need answers.
Which null-related bugs should the platform prevent?
Read the research →Compare .NET Span and Memory with more uniform lifetime and access guarantees. A bounded-view contract has to account for aliasing, GC, escaping references and interop.
Where do today’s memory-view APIs get in your way?
Read the research →Revisit function types alongside the existing .NET-style nominal delegate model. Captures, variance, events and type identity all affect the choice; no replacement is selected.
Which callback patterns should feel simpler?
Read the research →Explore runtime-owned suspension alongside .NET’s compiler-lowered async model, while keeping Task-shaped APIs in view. Scheduling, cancellation, cleanup and debugging remain design questions.
What should async preserve, and what should change?
Read the research →Build on the explicit UTF-8 methods while comparing .NET’s UTF-16 contracts. Scalar iteration, grapheme-aware operations and interop need distinct units and clear conversion costs.
Which text-processing examples should guide the APIs?
Read the research →Review the capability prototype alongside .NET collection interfaces. Read-only variance and immutable or frozen collections need separate contracts, with migration costs made explicit.
Which access guarantees do your APIs need?
Read the research →Explore injectable clocks alongside .NET TimeProvider and Noda Time. Preview 8 includes Clock.Now, SystemClock, Instant and Duration. Configurable calendars, time zones, monotonic timing and the wider API shape remain open.
What would make time-dependent code easier to test?
Read the research →Explore compiling IL to native code during execution, as .NET’s JIT does, while preserving the platform’s contracts. The current backend is an interpreter; no JIT implementation or replacement decision is selected.
Which workloads should guide a future compiler?
Read the execution direction →Evaluate the initial mark-and-sweep collector against .NET’s generational and compacting approaches. Better diagnostics, pause behavior and memory use need measurement; moving or concurrent collection brings additional reference and interop costs.
Which memory workloads and diagnostics matter most?
Read the GC direction →10 — FEEDBACK & DISCUSSION
Join the discussion about the runtime and APIs. Which contracts work well? What is missing or awkward? Bring a use case, compare an API with .NET, or suggest a different approach. You do not need to contribute code to help shape neoCLR.
Tell us what feels useful, what feels awkward, or where .NET already solves the problem well. A small program, an API comparison or a real-world use case is a great place to start.
Share a use case, ask a question, or challenge a design choice. GitHub Issues is one way to join the conversation.
.NET/CLR inspires the runtime model and familiar API concepts. Rust’s Result/Option model is a useful precedent for typed outcomes. Raven brings the language and tooling; projects such as Noda Time inform ongoing API research. Read the research approach →
This is a proof of concept, not a production runtime or a drop-in .NET replacement. The Raven importer supports bounded application classes, interfaces, inheritance and lambda captures. Generic application hierarchies, unrestricted reflection, nullable metadata and cleanup during terminal faults remain outside its current scope. The dedicated neoCLR tasks provide build/run; the normal Raven toolbar does not implement this target.
The earlier Neo concept language is preserved as development history. Raven is the language used for the current integration.