Jock — a language for Nock
A Nock combinator language
Jock
Jock compiles a high-level syntax to Nock,<br>the twelve-opcode combinator calculus underneath Nockchain and<br>Urbit, among other platforms. Jock models its syntax on Swift<br>and Rust, aiming to be more approachable than Hoon while still permitting direct<br>expression of Nock concepts: classes, generics, pattern matching and operator traits,<br>all the way down to raw nouns.
1. The public alpha is<br>the original release, from zorp-corp.
2. Development of a release candidate continues in a<br>successor repository (currently private),<br>expanding the language's features and capabilities.
01 » Compiles to Nock
Every Jock program erases to a noun and a formula. Structs become right-nested tuples,<br>unions become tagged cells, and the whole language bottoms out in twelve instructions.
Jock→ Nock
var a: Bool = true;<br>a = false;<br>compiles to
[8 [1 0] 7 [10 [2 1 1] 0 1] 0 2]<br>Write real Jock programs first, only meeting<br>the Nock ISA noun underneath when it starts to matter.
02 » Examples
A few programs from the examples folder —<br>each one a complete, executable program.
point.jock — operator traits on a sealed class
operator traits, classes, Real — p + q dispatches through class conformance
struct PointState { x: Real, y: Real };<br>class Point(PointState) impl Add, Sub {<br>add(self: Self, p: Self) -> Self {<br>Point{ x: self.x + p.x, y: self.y + p.y }<br>};<br>sub(self: Self, p: Self) -> Self {<br>Point{ x: self.x - p.x, y: self.y - p.y }<br>};<br>};<br>let p = Point{ x: 101.0, y: 105.0 };<br>let q = Point{ x: 42.0, y: 7.0 };<br>(p + q).x
sort.jock — generic insertion sort
bounded generics, T: Lt, operators as bounds — the same becomes a trait call
func(T: Lt) insert(x: T, xs: List(T)) -> List(T) {<br>match xs {<br>(h, t) => if x [x];<br>};<br>func(T: Lt) isort(xs: List(T)) -> List(T) {<br>match xs {<br>(h, t) => insert(h, isort(t));<br>_ => ~;<br>};<br>isort([5, 3, 8, 1, 9, 2])
stoplight.jock — a state-machine kernel
a zero-ceremony NockApp kernel — the compiler synthesizes load/peek/poke from the Kernel protocol
union Color { red, green, yellow };<br>struct LightState { c: Color };<br>func advance(c: Color) -> Color {<br>match c {<br>red => Color.green;<br>green => Color.yellow;<br>yellow => Color.red;<br>};<br>class Light(LightState) impl Kernel {<br>load(self: Light, old: *) -> Light { old as! Light };<br>peek(self: Light, p: *) -> * { self.c };<br>poke(self: Light, e: *) -> (List(Effect), Light) {<br>let n = advance(self.c);<br>(~, Light{ c: n })<br>};<br>};<br>Light{ c: Color.red }
counter.jock — the zero-ceremony counter kernel
impl Kernel, state threading — no driver, no import: this kernel builds and runs unchanged under jojo, the generic NockApp host
struct KS { n: @ };<br>class Counter(KS) impl Kernel {<br>load(self: Counter, old: *) -> Counter { old as! Counter };<br>peek(self: Counter, p: *) -> * { self.n };<br>poke(self: Counter, e: *) -> (List(Effect), Counter) { (~, Counter{ n: self.n + 1 }) };<br>};<br>Counter{ n: 0 }
counter-v2.jock — the upgrade target for counter
load as migration — same host, a new state shape; load's as! carries counter's exported state into total
struct KS2 { n: @, total: @ };<br>class CounterV2(KS2) impl Kernel {<br>load(self: CounterV2, old: *) -> CounterV2 { CounterV2{ n: old as! @, total: 100 } };<br>peek(self: CounterV2, p: *) -> * { (self.n, self.total) };<br>poke(self: CounterV2, e: *) -> (List(Effect), CounterV2) { (~, CounterV2{ n: self.n + 1, total: self.total + 1 }) };<br>};<br>CounterV2{ n: 0, total: 100 }
03 » Repositories
TrackRepositoryStatus
Alpha<br>zorp-corp/jock-lang<br>public (original release)
Recommenced<br>sigilante/jock<br>private (ongoing development)
Jock is a one-man rewrite in active development. The public alpha<br>remains the reference for the original language release by Zorp in<br>June 2025; new feature work is happening in the recommenced<br>repository as of August 2026.
04 » Keywords
Every keyword in the frozen 1.0 grammar — kernel forms lower to one Nockasm<br>production apiece, sugar rewrites to an AST already covered here — each with<br>a minimal example.
KeywordSyntax
letlet x: @ = 5;<br>varvar b: @ = 0; b = b + 1;<br>funcfunc inc(n: @) -> @ { n + 1 };<br>lambdalet f = lambda(x: @) -> @ { x + 1 };<br>if / elseif a == 3 { 42 } else { 17 }<br>matchmatch a { %1 => 0; _ => 84; }<br>switchswitch a { 1 => 0; _ => 84; }<br>loop / recurloop { if a == b { b } else { b = b + 1; recur } }<br>evaleval(subj, form)<br>crash_ => crash;<br>importimport urbit;<br>as / as? / as!(n as? Char) ?? 'x'<br>assertassert n > 0;<br>printprint("count = {n}");<br>and / or / xor / notif a and not b { 1 } else { 0 }<br>structstruct PointState { x: Real, y: Real };<br>class / implclass Point(PointState) impl Arithmetic { … };<br>traittrait Arithmetic { add(self: Self, other: Self) -> Self; };<br>aliasalias Number Real;<br>unionunion Shape { point, circle(Real) };<br>inx in s
defer is reserved (%defer) but its runtime semantics aren't pinned<br>yet, so it's left off the table above rather than shown with an invented example.
see supporters »