Skip to main content
Open source language for .NET

N#

Simple by design. Powerful by .NET.

C#
using System;
using System.Linq;

namespace MyApp;

public class Program
{
public static void Main()
{
var name = "World";
Console.WriteLine($"Hello, {name}!");

var numbers = new[] { 1, 2, 3, 4, 5 };
var doubled = numbers
.Select(x => x * 2)
.ToList();

foreach (var num in doubled)
{
Console.WriteLine(num);
}
}
}
N#
import System
import System.Linq



func Main() {
name := "World"
print $"Hello, {name}!"

let numbers = [1, 2, 3, 4, 5]
doubled := numbers
.Select(x => x * 2)
.ToList()

foreach num in doubled {
print num
}
}

Why N#?

The best parts of Go's simplicity, built for the .NET ecosystem.

Discriminated Unions

First-class union types with exhaustive pattern matching. The compiler catches missing cases at build time.

union Shape {
Circle { radius: double }
Rect { width: double, height: double }
}

func Area(s: Shape): double {
return match s {
Shape.Circle { radius } => 3.14 * radius * radius,
Shape.Rect { width, height } => width * height
}
}

Duck Interfaces

Structural typing without boilerplate. If a type has the right methods, it matches the interface.

duck interface IReader {
func Read(): string
}

class FileReader {
func Read(): string {
return "file contents"
}
}

// FileReader matches IReader automatically
func Process(r: IReader) {
print r.Read()
}

Go-Style Syntax

Short variable declarations with :=, no semicolons, convention-based visibility. Less noise, more signal.

func Main() {
name := "Alice" // type inferred
let items = [1, 2, 3] // immutable binding
count: int = 0 // explicit type

for item in items {
count += item
}

print $"{name}: {count}" // string interpolation
}

Pragmatic C# Interop

Use NuGet packages and call C# libraries in covered interop scenarios. N# is designed for CLR integration, with limitations documented instead of hidden.

import Microsoft.AspNetCore.Builder

func main(args: string[]) {
builder := WebApplication.CreateBuilder(args)
app := builder.Build()

app.MapGet("/", () => "Hello from N#!")
app.MapGet("/json", () => new {
Message: "Works with ASP.NET Core"
})

app.Run()
}

Tooling That Is Becoming the Product

A broad pre-release CLI surface modeled on Go/Rust inner loops, with structured JSON on the key automation commands used by agents.

nlc CLI

A broad pre-release CLI surface modeled on Go/Rust inner loops: check, format, lint, test, perf reports, and related project commands.

nlc check && nlc format --check && nlc lint

VS Code Extension

The public installer adds the N# VS Code extension when the code CLI is available, with the language server bundled for editor features.

code --install-extension nsharp.nsharp
🤖

LLM-First Queries

`check`, `fix`, `query`, and `lint` default to structured JSON; other commands expose JSON where implemented for automation and AI agents.

nlc query inspect --file main.nl --pos 10:5
🔧

Auto-Fix

Compiler-suggested fixes for supported scenarios such as missing imports and cleanup. Use --dry-run in CI or review before applying.

nlc fix --dry-run
📦

Dependency Management

Add, remove, update, audit, and visualize your NuGet dependencies. Detect unused packages with nlc tidy.

nlc add Serilog@3.1.1 && nlc tree
🎯

Static Analysis

Static analysis covers supported rules such as unused variables, missing imports, async-without-await, and unreachable code. Verify rule coverage against current nlc help/tests.

nlc lint --text

Quick Start

One copied command installs nlc, templates, SDK restore support, the language server, and VS Code tooling when VS Code is on PATH.

Install
curl -fsSL https://raw.githubusercontent.com/schneidenbach/nsharplang/main/scripts/install.sh | bash && . "$HOME/.nsharp/env"
nlc doctor
nlc new MyApp
cd MyApp
nlc run

N# in Action

Real code, real syntax. No pseudocode.

Hello WorldBasics
import System

func Main() {
name := "World"
print $"Hello, {name}!"
}
Error HandlingGo-style
func Divide(a: int, b: int): int {
if b == 0 {
throw new Exception("divide by zero")
}
return a / b
}

func Main() {
result, err := Divide(10, 0)
if err != null {
print $"Error: {err.Message}"
}
}
Union + Pattern MatchingTypes
union Result {
Success { value: int }
Failure { error: string, code: int }
}

func Handle(r: Result): string {
return match r {
Result.Success { value } =>
$"OK: {value}",
Result.Failure { error, code } =>
$"Error {code}: {error}"
}
}
LINQ PipelineCollections
import System.Linq

func Main() {
let names = ["Alice", "Bob", "Charlie", "Dave"]

result := names
.Where(n => n.Length > 3)
.Select(n => n.ToUpper())
.ToList()

foreach name in result {
print name
}
}
Records & ClassesTypes
record Point {
X: int
Y: int
}

class Logger(name: string) {
func Log(msg: string) {
print $"[{name}] {msg}"
}
}

func Main() {
p := new Point { X: 10, Y: 20 }
p2 := p with { X: 30 }
print $"({p2.X}, {p2.Y})"
}
Built-in TestingTooling
func Add(a: int, b: int): int => a + b

test "Add returns correct sum" {
assert Add(2, 3) == 5
assert Add(-1, 1) == 0
assert Add(0, 0) == 0
}

test "Add handles large numbers" {
result := Add(1000000, 2000000)
assert result == 3000000
}