A minimal, functional-first programming language that compiles to native binaries via LLVM.
No parens for function calls (add 1 2), indentation-based blocks, uppercase constructors, ADTs, pattern matching, immutable by default, with Hindley-Milner type inference.
Status: Alpha. Expect bugs, missing features, and rough edges. Feedback and contributions welcome.
git clone https://github.com/Adjanour/ko-language.git
cd ko-language
./build.sh
echo 'fn main = println "Hello, Kō!"' > hello.ko
./ko-dist/ko hello.koNew to functional programming? Read the Getting Started Guide — it's written for Python/JS/C programmers.
Build a self-contained folder you can move anywhere — one binary, the full standard library, and example programs. No install, no system-wide dependencies at runtime.
./build.shThis produces ko-dist/:
ko-dist/
├── bin/
│ ├── ko # compiler binary
│ └── ko-lsp # language server
├── std/ # standard library (Bool, Int, Float, String, List, Math)
├── examples/ # example .ko programs
├── editors/
│ ├── vscode/ # VS Code extension
│ └── tree-sitter/ # tree-sitter grammar + queries
└── ko -> bin/ko # convenience symlink
Drop it anywhere and run:
cp -r ko-dist ~/ko
~/ko/ko hello.koNo npm install. No brew. No virtualenv. One folder.
Requires Zig 0.17 and LLVM 22.
# Ubuntu/Debian
sudo apt install llvm-22-dev zlib1g-dev
# Arch
sudo pacman -S llvm zlib
git clone https://github.com/Adjanour/ko-language.git
cd ko-language/ko-zig
zig buildbrew install llvm@22
export PATH="/opt/homebrew/opt/llvm@22/bin:$PATH"
git clone https://github.com/Adjanour/ko-language.git
cd ko-language/ko-zig
zig buildNot yet supported. LLVM-22 has no prebuilt Windows packages and the JIT (MCJIT) doesn't support Windows. AOT-only support is planned for a future release.
Kō ships with skills for AI assistants. Install one so your AI can write Kō:
# OpenCode
cp -r ko-zig/skills/ko-language ~/.config/opencode/skills/ko-language
# Claude Code / Copilot — copy to project root
cp ko-zig/skills/ko-language/CLAUDE.md /path/to/your/project/CLAUDE.md
# Cursor — copy to project root
cp ko-zig/skills/ko-language/.cursorrules /path/to/your/project/.cursorrulesSee ko-zig/skills/README.md for details.
The compiler binary is at zig-out/bin/ko. Run tests with:
zig build test --summary all- Lexer: hex (
0xFF), underscores, comments, operators, string literals - Parser: ADTs, pattern matching, functions, let bindings, if/then/else, lambdas, tuples, records, modules, imports, pipe operator, named args, type annotations
- Typechecker: Hindley-Milner type inference, let-polymorphism, ref types, type annotations
- Codegen: LLVM IR via kassane/llvm-zig bindings; JIT execution and AOT compilation (
--emit-obj,--emit-exe) - Memory management: Reference counting for heap-allocated objects (tuples, records, constructors, closures)
- Currying: Multi-param functions support partial application
- Modules:
module Namedefinitions withpubvisibility - Stdlib: Bool, Int, Float, String, List, Math — built-in, no import needed
- LSP:
ko-lspbuilt alongside the compiler, with error locations - REPL:
ko --replfor interactive evaluation - Module imports:
import std.Math.{abs, max}— file-based, with selective imports - Error propagation:
?operator unwrapsOkor propagatesErr - 78 of 78 tests passing
ko <file.ko> Run program (default)
ko --repl Start interactive REPL
ko --dump-ir <file.ko> Show generated LLVM IR
ko --emit-ir <out> <file> Write LLVM IR to file
ko --emit-obj <out> <file> Compile to object file
ko --emit-exe <out> <file> Compile to executableNo args shows help. Errors include file and location:
error at hello.ko:1:11: undefined name 'x'
# Functions
fn add x y = x + y
fn apply f x = f x
# Pattern matching
type Maybe a = Just a | Nothing
fn from-just default mx =
match mx
Just x => x
Nothing => default
# Records
type Point = { x: Int, y: Int }
let p = Point { x = 3, y = 4 }
# Tuples
let t = (1, "hello", true)
# Lambda closures
let x = 10
let f = \y -> x + y
f 5 # 15
# Pipe operator
5 |> add 1 |> add 2 # 8
# Partial application
let add1 = add 1
add1 2 # 3
# References (mutable)
let r = ref 42
r := 100
!r # 100
# Compile-time evaluation
comptime fn factorial n =
if n == 0 then 1 else n * factorial (n - 1)
comptime fn sum lst =
match lst
| Cons x rest => x + sum rest
| Nil => 0
let x = comptime factorial 5 # 120, evaluated at compile time
let total = comptime sum xs # list operations at compile time
# Module imports
import std.Math.{abs, max}
abs (-5) # 5
# Error propagation
type Result a b = Ok a | Err b
fn divide a b =
if b == 0 then Err "division by zero"
else Ok (a / b)
fn compute x y =
let a = divide x y? # unwraps Ok, propagates Err
Ok a
Kō ships with a language server (ko-lsp) and tree-sitter grammar. See the full Editor Setup Guide for detailed instructions.
Quick start:
- VS Code:
code --install-extension vscode-ko/ko-language-0.5.0.vsix - Neovim: use
nvim-lspconfigwithko-lsp+nvim-treesitterfor highlights - Helix: add
ko-lspto~/.config/helix/languages.toml - Vim: use
vim-lsporCoC.nvim— see guide
- Getting Started — Start here if you're new to functional programming
- Language Charter — canonical vision and syntax freeze
- Formal Grammar — EBNF spec
- Idiomatic Programs — example programs
- Crash Course — functional programming from scratch
- Ko by Example — step-by-step guide
- Core Concepts — language concepts
- Quick Reference — syntax cheat sheet
- No parens for function calls —
add 1 2notadd(1, 2) - Minimal indentation — only for function bodies and blocks
- Uppercase = constructors —
Justvsx typefor ADTs and records —type Maybe a = Just a | Nothing- Immutability by default —
reffor explicit mutation matchwith=>— exhaustive pattern matching!for deref,notfor boolean negation|>pipe operator — left-to-right function application- Reference counting — automatic memory management for heap objects
comptimeexpressions — evaluate code at compile time with pattern matching, constructors, and built-in string/list operations
- Inline comments after
letbindings have a scoping bug. The parser no longer crashes, but a comment between a let value and its body silently produces incorrect scoping. Workaround: put comments on their own line. println (fn_call)prints<fn>for function-typed values. This is correct behavior — functions aren't printable. Extract to aletbinding first:let n = f x; println n- Multi-line match bodies confuse the parser. A multi-line
matchfollowed by another expression can cause the next expression to be swallowed. Workaround: extract the match into a helper function. - Multi-line closures with captured variables cause codegen errors. Workaround: use single-line lambdas or extract to a
letbinding. - Partial application only works for global
fndefinitions. Multi-param lambdas (\x y -> ...) do not get partial application.
- Imported module type info doesn't propagate to the main typechecker — shows type variables instead of concrete types. This is an architectural limitation of the current import system.
- No circular import detection. Recursive imports silently produce incorrect results.
- No package/module system — just flat file imports with
stdlibrary support.
- LSP is single-file only. No cross-file go-to-definition or import navigation.
- LSP document symbols always report line 0. Source location for symbols is not yet implemented.
- REPL doesn't support multi-line input. Each line is processed independently.
- REPL can't evaluate top-level
letbindings. Usefndefinitions instead.
| Platform | JIT | REPL | LSP | --emit-obj |
--emit-exe |
|---|---|---|---|---|---|
| Linux x86_64 | Yes | Yes | Yes | Yes | Yes |
| Linux aarch64 | Partial | Yes | Yes | Partial | No |
| macOS (Apple Silicon) | Yes | Yes | Yes | Yes | Yes |
| macOS (Intel) | Yes | Yes | Yes | Yes | Yes |
| Windows | No | No | No | No | No |
Linux x86_64 is the primary development platform. All features work.
macOS requires brew install llvm@22 and adding LLVM to PATH. JIT, LSP, and AOT all work. See Installation for details.
Linux aarch64 needs: dynamic CPU detection, aarch64 linker paths, and aarch64 data layout.
Windows is not yet supported. LLVM-22 has no prebuilt Windows packages and MCJIT doesn't support Windows. AOT-only support is planned.
- Lexer, parser, typechecker, codegen
- Reference counting (heap-allocated objects)
- Recursive ADTs (binary trees, lists)
- Stack overflow detection
- Partial application / currying
- LSP server with error locations
- REPL with pretty-printing
- File-based module imports (
import std.Math) -
?operator for Result error propagation - Result built-in operations
- Generics (monomorphization)
- Record type syntax with field access
- Trait/typeclass system
- Module system v2 (hierarchical imports, first-class modules)