ZScheme is a Scheme-like functional programming language that compiles to .NET. It combines S-expression syntax with static type inference, immutable data structures, and full CLR interoperability.
- Implement data structures using existing .NET types
- Provide a usable Scheme-like language that compiles to efficient .NET code
- Provide APIs that are similar to Racket
- Polished editor support using language server protocol
- Static type inference — Hindley-Milner type system with unification; no type annotations required (but supported)
- Immutable by default — Lists, hashes, and vectors backed by .NET immutable collections
- Pattern matching — Destructuring with exhaustiveness checking
- Algebraic data types — Records, discriminated unions, and tuples
- First-class functions — Closures, higher-order functions, partial application, and composition
- Tail call optimization — Recursive functions unrolled automatically
- Result and Option types — Functional error handling built into the standard library
- Macros —
define-syntaxwithsyntax-rulesfor compile-time code generation - Async/await — Async functions backed by .NET Tasks
- CLR interop — Call .NET methods, construct objects, implement interfaces
- Object system — Classes with inheritance, interfaces, method dispatch, and anonymous classes
- Two code generation backends — Emit C# source or IL directly (via AsmResolver)
- Package system — Declare dependencies, build, test, and install packages
- NuGet integration — Reference NuGet packages directly from package manifests
- Built-in test framework — xUnit-based ZUnit with rackunit-style assertions
- Continuations support (e.g.
call/cc) - ASP.NET wrapper package
- Editor support: Sublime Text, Visual Studio Code, Zed, JetBrains
- Mutable properties on record types
- Symbols data type(s)
- Built-in language formatter tool (similar to
raco fmt) - Documentation: Showing doc comments in the LSP, HTML doc generation
dotnet build# Compile a .zs file to an executable
zs compile examples/factorial.zs -o out
# Compile and run in one step
zs run examples/factorial.zs
# Start the REPL
zs replOr via dotnet run without installing:
dotnet run --project src/ZScheme.Cli -- compile examples/factorial.zs -o out(define (add [x : Int] [y : Int]) : Int
(+ x y))
;; Type annotations are optional — the compiler infers them
(define (double x)
(* x 2))
;; Lambdas
(define (make-adder [n : Int]) : (Int -> Int)
(lambda (x) (+ n x)))(define-record Point [x : Int] [y : Int])
(define-union Shape
(Circle [radius : Int])
(Rect [w : Int] [h : Int]))(define (area [s : Shape]) : Int
(match s
[(Circle r) (* r r)]
[(Rect w h) (* w h)]))The compiler checks that all cases are covered and reports unmatched patterns.
(import stdlib/treelist)
(import stdlib/hash)
;; Immutable AVL-backed tree list
(define nums (treelist 1 2 3 4 5))
(treelist-map nums (lambda (x) (* x 2)))
(treelist-filter nums (lambda (x) (> x 2)))
(treelist-fold nums 0 (lambda (acc x) (+ acc x)))
;; Immutable hash table
(define scores (hash (pair "alice" 95) (pair "bob" 87)))(import stdlib/result)
(import stdlib/error)
(define (safe-div [a : Int] [b : Int]) : (Result Int Error)
(if (= b 0)
(Err (make-error "division by zero"))
(Ok (/ a b))))(define-async (fetch-and-add [x : Int]) : (Task Int)
(let [result (await (compute-async x))]
(+ result 10)))(define-syntax define-dto
(syntax-rules ()
[(define-dto name field ...)
(define-record name field ...)]))
(define-dto UserInfo [name : String] [age : Int]);; Static methods: Type/Method with slash separator
(import-clr
[writeln System.Console/WriteLine])
(writeln "Hello from .NET!")
;; Instance methods: Type.Method with :instance flag and type annotation
(import-clr
[sb-tostring System.Text.StringBuilder.ToString
:instance : (System.Text.StringBuilder -> String)])
(let [sb (new System.Text.StringBuilder "hello")]
(sb-tostring sb))(import stdlib/string)
;; Base class must be marked #:open to allow subclassing
(define-class #:open Animal
[name : String]
[sound : String]
(define (Speak) : String
(format "{0} says {1}" name sound)))
(define-class #:open Dog : Animal
[breed : String]
(define (Speak) : String
(format "{0} the {1}" name breed)))(import zunit)
(test-suite MathTests
(test-case addition
(check-equal? (+ 1 2) 3))
(test-case subtraction
(check-equal? (- 3 1) 2)))| Command | Description |
|---|---|
compile <file.zs> |
Compile a ZScheme source file |
build |
Build a package from its manifest |
test |
Run package tests |
run <file.zs> |
Compile and execute a file |
install |
Compile and cache a library package |
repl |
Start the interactive REPL |
package init |
Initialize a new package |
generate-project |
Generate a .csproj from a package |
| Option | Description |
|---|---|
-o, --output <dir> |
Output directory |
-b, --backend cs|il |
Code generation backend (C# source or IL) |
--ref <dir> |
CLR assembly reference directory (repeatable) |
--module-path <dir> |
Additional module search path (repeatable) |
--package-path <dir> |
Register a package for qualified imports (repeatable) |
--precompiled <path> |
Reference a precompiled .dll (repeatable) |
--debug |
Enable compiler debug logging |
The standard library (stdlib) provides modules imported with qualified names:
| Module | Description |
|---|---|
stdlib/option |
Option type — Some and None |
stdlib/result |
Result type — Ok and Err |
stdlib/error |
Error record and make-error constructor for structured errors |
stdlib/list |
Pure singly linked list (List, Cons, Nil) with map, filter, fold, ... |
stdlib/treelist |
AVL-tree-backed immutable list (treelist-map, treelist-filter, treelist-fold, ...) |
stdlib/vector |
Immutable vector operations |
stdlib/hash |
Immutable hash table operations |
stdlib/string |
String utilities |
stdlib/math |
Math functions |
stdlib/datetime |
Date and time utilities |
stdlib/task |
Async task helpers |
stdlib/catch |
Exception-to-Result conversion |
stdlib/mutable/* |
Mutable collection variants |
stdlib/concurrent/* |
Thread-safe concurrent collections |
Packages are defined with a .zspkg manifest:
(package
(name "my-package")
(version "0.1.0")
(import-prefix "mylib")
(sources
(main "src")
(test "test"))
(dependencies
(nuget
[System.Collections.Immutable "9.0.0"]))
(test-dependencies
(zscheme
[zunit :local "../zunit"]))
(build
(main
(namespace "MyLib"))))src/ZScheme.Cli/ CLI entry point and REPL
src/ZScheme.Compiler/ Core compiler (lexer, parser, type checker, IR, codegen)
packages/stdlib/ Standard library
packages/zunit/ Testing framework
packages/http/ HTTP client library
examples/ Example programs
tests/ Compiler test suite
# Run compiler tests
dotnet test
# Run package tests (stdlib, http)
pwsh ./run-package-tests.ps1
# Build all examples
pwsh ./build-examples.ps1- F# — ML-family language on .NET with algebraic data types, pattern matching, and type inference
- Racket — Scheme descendant with a rich macro system and language-oriented programming
- Typed Racket — Racket’s gradually-typed sister language which allows the incremental addition of statically-checked type annotations
- Plait — Statically typed teaching language built in Racket