Language Tour
Learn lykn in 10 Examples
Each example shows the lykn source and its compiled JavaScript output.
No runtime, no magic — just parentheses in, clean JS out.
1 · Bindings
Hello, World
bind creates an immutable binding. It compiles to const — always. Colon syntax (console:log) compiles to dot notation.
lykn
(bind greeting "Hello, World!")
(console:log greeting)
javascript
const greeting = "Hello, World!";
console.log(greeting);
2 · Functions
Named Functions with Contracts
func defines named functions with keyword-labeled clauses. Type annotations compile to runtime checks in development, stripped in production.
lykn
(func greet
:args (:string name)
:returns :string
:body
(template "Hello, " name "!"))
(console:log (greet "world"))
javascript
function greet(name) {
if (typeof name !== "string")
throw new TypeError(/* ... */);
const result = `Hello, ${name}!`;
return result;
}
console.log(greet("world"));
3 · Lambdas
Anonymous Functions
fn creates arrow functions. Type annotations work the same way. Use them everywhere a callback is needed.
lykn
(bind numbers #a(1 2 3 4 5))
(bind doubled
(numbers:map
(fn (:number x) (* x 2))))
(console:log doubled)
;; → [2, 4, 6, 8, 10]
javascript
const numbers = [1, 2, 3, 4, 5];
const doubled =
numbers.map(
(x) => x * 2);
console.log(doubled);
// → [2, 4, 6, 8, 10]
4 · Closures
Functions Remember Their Context
Because bind is immutable, closures are safe. The captured value never changes behind the function's back.
lykn
(func make-greeter
:args (:string prefix)
:returns :function
:body
(fn (:string name)
(template prefix ", " name "!")))
(bind hello (make-greeter "Hello"))
(console:log (hello "World"))
;; → "Hello, World!"
javascript
function makeGreeter(prefix) {
return (name) =>
`${prefix}, ${name}!`;
}
const hello = makeGreeter("Hello");
console.log(hello("World"));
// → "Hello, World!"
5 · Types
Algebraic Data Types
type defines variants with named, typed fields. Each variant compiles to a constructor function returning a tagged object. Zero-field variants are constants.
lykn
(type Shape
(Circle :number radius)
(Rect :number w :number h)
(Point))
(bind s (Circle 5))
;; → { tag: "Circle", radius: 5 }
javascript
function Circle(radius) {
/* type check in dev */
return { tag: "Circle", radius };
}
function Rect(w, h) {
return { tag: "Rect", w, h };
}
const Point = { tag: "Point" };
const s = Circle(5);
6 · Pattern Matching
Exhaustive match
The compiler ensures every variant is handled. Add a variant to Shape and forget to update the match? Compile error — not a runtime surprise.
lykn
(func area
:args (:any s)
:returns :number
:body
(match s
((Circle r) (* Math:PI r r))
((Rect w h) (* w h))
((Point) 0)))
javascript
function area(s) {
if (s.tag === "Circle") {
const r = s.radius;
return Math.PI * r * r;
}
if (s.tag === "Rect") {
return s.w * s.h;
}
return 0;
}
7 · Mutation
Cells — Controlled Mutable State
Mutation is explicit and visible. cell creates a mutable container. swap! updates it. express reads it. Every mutation wears a ! suffix.
lykn
(bind count (cell 0))
(swap! count
(fn (:number n) (+ n 1)))
(console:log (express count))
;; → 1
javascript
const count = { value: 0 };
count.value =
((n) => n + 1)(count.value);
console.log(count.value);
// → 1
8 · Threading
Data Pipelines
Threading macros turn nested calls inside-out. -> threads as the first argument; ->> threads as the last. some-> short-circuits on null.
lykn
(bind result
(->> #a(1 2 3 4 5 6 7 8 9 10)
(:filter (fn (:number n)
(= (% n 2) 0)))
(:map (fn (:number n)
(* n 10)))
(:reduce + 0)))
javascript
const result =
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
.filter((n) => n % 2 === 0)
.map((n) => n * 10)
.reduce(
(a, b) => a + b, 0);
// → 300
9 · Modules
ES Modules
Standard ESM. Named imports only — no namespace imports, no CommonJS. Explicit dependencies, always.
lykn
(import "./math.js" (add multiply))
(export
(func calculate
:args (:number x :number y)
:returns :number
:body (add (multiply x y) x)))
javascript
import { add, multiply } from "./math.js";
export function calculate(x, y) {
return add(multiply(x, y), x);
}
10 · All Together
Option and Result
Lykn provides Option and Result types for safe error handling. No null checks, no thrown exceptions for expected failures — just data and pattern matching.
lykn
(type Option
(Some :any value)
None)
(func find-user
:args (:number id)
:returns :any
:body
(if (= id 1)
(Some (obj :name "Duncan"))
None))
(match (find-user 1)
((Some user)
(console:log user:name))
(None
(console:log "not found")))
javascript
function Some(value) {
return { tag: "Some", value };
}
const None = { tag: "None" };
function findUser(id) {
if (id === 1)
return Some({ name: "Duncan" });
return None;
}
const t = findUser(1);
if (t.tag === "Some")
console.log(t.value.name);
else
console.log("not found");
Ready to Build?
Install lykn and start writing. The full book covers everything from here through macros, the browser, servers, and the compiler internals.