A language for building microservices and enterprise software, with dependency injection, function intent, and refined types as first-class citizens. Compiled to native binaries.
Ideas that usually live in frameworks and conventions are, in Ξ, features of the language itself.
Constraints live in the type. Values are checked at construction, so a partially-valid instance can never exist.
Number where value >= 0
Every function declares its kind: mapper, predicate, consumer, producer and more. Purity and effects are part of the signature, and the compiler enforces them.
mapper · predicate · consumer
Declare
deps { }
and the compiler wires it. No container, no
registration, no reflection.
One name, many bodies: the first
where
guard that holds wins. It is exactly how
std/web routing works.
where res.status == 200
Business rules as first-class DxT tables, alongside atoms, state machines, interrupts and pub/sub events.
DxT · atoms · machines ↓
T!
result types,
ok/err, and
?
propagation. Failures are values you handle,
not surprises.
let row = load(id)?
async
spawns a worker and returns a Future
immediately.
await all
joins them, in order.
await all jobs
Absence is typed: optionals (T?), a
none
literal, and
empty
zero values.
null
does not exist.
mapper top() -> Player?
Algebraic types with payload-binding
match. Lowered to plain tagged unions.
match s { Circle c -> ... }
Resumable conditions. A function
signals and suspends while the caller decides
whether to resume or abandon it.
Built-in pub/sub. Producers publish a DTO;
listeners receive the typed value. No JSON inside
the process.
Compiles to a standalone binary, or to
WebAssembly with
--target wasm. No VM, no garbage collector.
xc app.xi → ./build/app
Every serious codebase ends up bolting on an IoC framework with annotations and a runtime registry. In Ξ the compiler does the injection: you declare what you need and the wiring is derived from your code.
A class lists what it needs in a
deps { }
block. It never news up its own
collaborators, so constructor
plumbing disappears from the
codebase.
Implementations are discovered and wired automatically at compile time. There is no container to configure and no reflection involved. A missing dependency is a build error rather than a runtime surprise.
The
entry
takes dependencies as parameters.
Classes, listeners and even decision
tables declare
deps. Lifetimes are explicit:
singleton
or
transient.
bind
is the override, not the rule
You only write
bind
to choose between implementations or
swap in a test double. Zero bindings
is the normal case.
Rules engines, state stores and state machines are language constructs in Ξ. All three are ordinary values, and all three are injectable.
A
decision
is a function kind whose body is a set
of
when → result
arms, read top to bottom until the first
match wins. The rule set your product
owner writes on a whiteboard is the
code.
when <cond> =>
<result>
arms, or a tabular grid of
in
columns and an
out
column
hit first,
hit unique,
hit collect
- optionally with an
aggregator
An atom holds an immutable
state
that can only change through
transition
reducers, so every change is named and
typed.
.current; the state is immutable and
there are no setters
transition
reducers take the state and
return a new one; call them with
just the extra args
.undo()
gives you time-travel for
free
A
machine
declares its
states, the
initial
one, and named transitions like
open : Closed -> Open. Machines are immutable: every
transition returns a new value.
where
guards and
update
clauses on every
transition
.can(...)
IllegalTransition, a resumable interrupt you can
recover from
A function
signals a condition and suspends. The
enclosing
try/catch
decides what happens next. Where an
exception unwinds the stack, an
interrupt lets the caller repair the
situation and continue.
interrupt
declares a typed condition with
a payload
recover
runs the restart and resumes at
the signal point
skip
abandons the suspended
computation instead
Producers
publish("topic", dto); a
listener
receives the typed value with zero
serialization in process. Bind a
transport to cross the network;
producers and listeners never change.
listener
kind subscribes with
on "topic"
and receives the typed
value
Events.run()
or
Events.runAsync()
PublisherService
/
ConsumerService
to go external
A typed web framework, a test runner, configuration and C interop ship with the toolchain. Building a service needs no packages.
Implement
WebRequestHandler
and overload
action handle(req, res)
with
where
guards - exact paths or
web.route
patterns. Params and bodies decode into
your types; responses serialize
themselves. HTTPS and HTTP/2 are opt-in
flags.
test
blocks are excluded from normal builds
and run with
xt. Tests get dependencies injected, and
module Test
bindings swap in test doubles without a
mocking library.
Config is an interface bound to a file:
bind AppConfig ->
readConfig("app.yaml"). YAML, JSON or XML, with hot-reload
included.
Port a C library by declaring it: an
extern "C"
block plus a
link
directive. The signatures are the binding.
List,
Map,
Set,
Stack,
Queue,
SortedQueue
are built-in generics. No imports needed.
math · text · json · yaml · xml · crypto · fs · net · http · thread · process · time
xc app.xicompile to a native binary
xirun tool + interactive REPL
xt calc_test.xirun the tests
xi installfetch module dependencies
xi packpackage a library for others
loadtest --bench app.xiload & perf testing
xc --target wasmcompile to WebAssembly
xi updateself-update the toolchain
xc --backend nativenative codegen, no cc or ld
eXstream is a music-streaming service built in Ξ: microservices behind an API gateway, JWT auth, a React front end, Docker deployment.
code-by-sia/eXstream ↗Functional pipelines over collections, typed configuration, cron jobs, health monitoring and OpenTelemetry tracing are language and standard-library features - not packages you bolt on.
map / filter / fold / reduce with { it } and { a, b => } lambdas, chained into a single pass - plus any, all, sumOf and joinToString.
Bind an interface to a file with readConfig; each method reads and decodes its matching key into your types. YAML, JSON or XML, with hot-reload - and swappable in tests.
A scheduled job runs a block on a cron string or a fixed every 5000ms interval. It declares its own deps, and the runtime keeps the program alive.
std/monitoring is a mechanism, not a fixed list: the registry loops every implementation you import - memory, cpu, web, query, thread, or your own. It stays off until enable(), so the HTTP endpoints answer 404 until you switch it on.
std/tracing gives spans a trace/span identity, parent-child nesting, attributes and events, W3C context propagation, and OTLP export to any OpenTelemetry collector. It sits alongside monitoring, needs no new runtime code, and is off until enable().
A query is a value, not a string. Build the plan once, then run it in memory, against a provider, or render it to SQL - and let a repository own the boundary.
A chain reifies into a typed plan instead of running where it is written. Hand the plan to a provider to execute it, or to std/sql to render it for SQLite, Postgres or MySQL. The plan is data; the provider decides.
Implement CrudRepository<TKey, TEntity, TModel> and supply just getProvider() and source(). findAll, findById, save, delete and the entity/model convert pair are inherited defaults you can override.
On macOS (Apple Silicon & Intel) and Linux.
You just need a C compiler on your
PATH.
Or grab a prebuilt tarball from the
releases page and put its
bin/
on your
PATH.
You get
xc, the compiler, and
xi, the run tool and REPL.
Save a file with the
.xi
extension. Every program has an
entry
and a
module.
One command compiles to a native binary and runs it.
A real editor. Pick an example or start blank, edit the code, and run - it compiles to WebAssembly on the Xi compile server and runs in your browser.