The same idea, a program that asks operations and handlers that answer them, is implemented outside Scala in nine languages: Java, Kotlin, Rust, Go, Goose, Zig, TypeScript, Python and Lean. Each takes the form its language allows: a program as data run by a machine, direct style on the language's own continuations, coroutines or generators, a function compiled into a state machine, or a plain function over a typed context. Three things hold them to one meaning: one digest of a thousand random programs, test suites carried from one repository to another, and the okay wire, on which a program in one language is answered by handlers in another.
As of 11 November 2026: the state of the default branches. Every statement on this page was checked in the repositories; where a file says it, the file is named. The Scala implementations have a page of their own.
Eleven repositories, all public. Three are released: okay-direct, okay-kotlin and okay-zig. Java and Rust appear more than once: each has a library and a kernel of the reference model.
| Repository | Language | How a program runs | The effects of a program are checked | Continuations | Status |
|---|---|---|---|---|---|
| okay-java | Java 25 | As data. Eff<L, A> is nodes run by a machine over an explicit stack of delimiters;
an operation is a shift0 to its handler's prompt. A second notation, okay.rows, on
the same machine: Comp<A, R>, the interface of okay-effects' Java kernel. A
Bridge runs a program of either notation inside the other. |
By javac. An effect is an interface at a level L; a handler's body is a generic method at a
level of its own; Eff.run takes a program at Top. In okay.rows the row
is a nested type, by position, as in okay-effects' Java kernel; run takes the empty row. |
Multi-shot: k is the captured part of the machine's stack. |
Public. No release. Last commit 11 Nov 2026. |
| okay-direct | Java 25 | In direct style: perform is a method call. Three implementations: HotSpot's internal
continuations (Loom), virtual threads, Espresso's continuations. |
By javac. A program takes a capability, Effect<F>, for each effect. A capability
found by key (Implicit) is checked at run time. |
One-shot on Loom and on threads. Multi-shot on Espresso: a resumption runs a copy of the frames. | Released, 0.1.0. Last commit 23 Oct 2026. |
okay-effects,
java/ |
Java 25 | As data: Comp<A, R>, one loop over a stack of levels, one level a handler. |
By javac. The row is a nested type, by position:
Comp<A, Eff<State<Integer>, Eff<Choose, ?>>>; run takes the
empty row. |
Multi-shot: k is immutable levels, shared. |
Public. Used from source. Last commit 11 Nov 2026. |
| okay-kotlin | Kotlin 2.4. JVM, JS, native | Two styles. Interfaces: an effect is an interface of suspend functions, on Kotlin's coroutines. Data:
Prog<F, R, A>, run by a CEK machine written after okay-scala's, or by the coroutines. |
By the compiler. Interfaces: context parameters. Data: rows and typed places. | Interfaces: k is resumed once; k.copy() copies the frames, on the JVM and JS,
not on native. Data on the machine: multi-shot on every platform. |
Released, 0.1.0. Last commit 23 Oct 2026. |
| okay-rust | Rust, edition 2024 | Three crates. okay: an async function over a context, the compiler's state
machine. okay-resumable: a macro makes a function an enum of its stops.
okay-machine: a program as data, Eff<R, A>. |
By rustc. okay: trait bounds on the context, Has<E, Ix>, written by
needs!. okay-machine: a row is a list of types. |
okay: one-shot, a future cannot be cloned. okay-resumable: multi-shot, the enum
is Clone. okay-machine: multi-shot, k is data. |
Public. Not on crates.io. Last commit 28 Oct 2026. |
okay-effects,
rust/ |
Rust, edition 2024 | As data, composed statically: impl Prog<A, eff![State<i64>, Choose]> is a struct
of structs, run by one loop. |
By rustc. The row is a list of types; run asks for the empty one. |
Multi-shot: K is Clone. A computation is run once, on one thread. |
Public. Used from source. Last commit 11 Nov 2026. |
| okay-go | Go 1.27 | In direct style on Go's coroutines (iter.Pull). Package prog: programs as
data. |
Not as a set. A program's effects are its capabilities, Cap[O], passed as parameters; an
operation's answer is typed. A capability used outside its handler is refused at run time. |
One-shot: a coroutine cannot be copied. Multi-shot in prog. |
Public. "An experiment" (its README). Last commit 18 Oct 2026. |
| okay-goose | Goose, which compiles to C | By a translator, okgc, written in Goose: an effect becomes operations over a context,
monomorphized; a resumable fn becomes a state machine. |
By okgc, at the definition: a function of type A ! F + G calls only the
operations of its effects. Then by Goose: a context without an operation is its error. |
By the effect's kind: tail, once, many. The rest is a struct,
copied; it can be written out as bytes. |
Public. No release. Last commit 28 Oct 2026. |
| okay-zig | Zig 0.17 | Two layers with one set of handlers. Contexts: a plain function, an operation an inlined call. The
machine: a program as data. And resumable: a union of a program's stops, written by hand. |
At compile time. ok.needs for a context; a row is a tuple of types, and an effect not in it
is a compile error. A guarded effect answers an error union. |
The machine: multi-shot. A resumable union is copied by value. | Released, 0.1.0. Last commit 28 Oct 2026. |
| okay-ts | TypeScript 7, Node 26 | As data on a machine: a node and a persistent stack of frames. A program is written as a generator or as data. | By tsc. The row is a union in the program's type; run() exists only for the empty row.
A cast passes it. |
A generator: once, and its type says so. Data: multi-shot. A handler that resumes many times takes data only. | Public. Not on npm. Last commit 15 Oct 2026. |
| okay-python | Python 3.11+ | On one machine with a persistent stack of frames. A program is written as a generator,
yield from cap.perform(op), or as data. |
Not as a set. Effects are capabilities passed as parameters. mypy --strict types an
operation's answer; which operations a capability takes is checked at run time. |
A generator: once. Data: multi-shot. A handler that resumes many times (resuming,
all_paths) takes data only; mypy --strict refuses a generator there. |
Public. Not on PyPI. Last commit 11 Nov 2026. |
| okay-lean | Lean 4.35 | As data: okay-simple's Free monad, ported. |
By Lean. The row is a list of effects in the type; run exists only for
A ! []. |
Multi-shot: k is a pure function. |
Public. No release. Last commit 11 Nov 2026. |
okay-direct also has front ends for Scala 3 and 2.13; they are on the Scala page. okay-rs is the earlier repository of okay-rust; its README says it has moved, and its history is in okay-rust. okay-effects is the reference model: one small kernel written three times, in Scala 3, Java 25 and Rust.
One rule holds across the family: a continuation resumed many times is a real value, a copy of frames, a cloned state machine or data; a body is never run again from its start. Where a language's own continuation cannot be copied (a generator in Python and in TypeScript), it is resumed once, and a program that must be resumed many times is written as data.
Copied from each repository's own documentation or tests; the file is named.
interface Ask<L> { Eff<L, Integer> number(); }
interface Asked<L, A> { <M extends L> Eff<M, A> in(Ask<M> a); } // a handler's body: a level of its own
static <L, A> Eff<L, A> asking(int n, Asked<L, A> body) {
return Eff.handle(new Eff.Body<L, A, A>() {
public <M extends L> Eff<M, A> in(Prompt<L, M, A> p) { return body.in(() -> p.shift0(k -> k.apply(n))); }
}, Eff::pure);
}
public sealed interface State<S> {
record Get<S>() implements State<S>, Op<State<S>, S> {}
record Put<S>(S value) implements State<S>, Op<State<S>, Void> {}
}
public void operation(State<S> op, Answer<State<S>> k) {
switch (op) {
case Get<S> get -> k.answer(get, state);
case Put<S> put -> { state = put.value(); k.answer(put, null); }
}
}
A program is plain Java that takes the capability:
static int total(Effect<Prices> prices, Effect<Fail<String>> fail, List<String> cart),
and performs inside a stream's lambda.
static Comp<Integer, Eff<State<Integer>, ?>> add(int x) {
return State.<Integer>get().flatMap(s -> State.put(s + x).map(done -> s + x));
}
static Comp<Integer, Eff<State<Integer>, Eff<Choose, ?>>> p() { // a state, and a choice under it
return up(Choose.among(List.of(1, 2))).flatMap(x -> add(x));
}
run(Choose.all(State.run(0, p()))); // [Final[state=1, value=1], Final[state=2, value=2]]
fun interface Ask {
suspend fun ask(): Int
}
context(a: Ask) suspend fun askFor(): Int = a.ask()
/** each ask answered with `n`, the rest resumed in the clause: a general handler in tail position */
suspend fun <A> answering(n: Int, body: suspend context(Ask) () -> A): A = handle { prompt ->
context(Ask { prompt.shift { k -> k(n) } }) { body() }
}
okay (README.md)okay::needs! {
async fn counted(cx: [Reader<i64>, State<i64>, Writer<String>], n: i64) -> i64 {
eff! {
d <- ask(cx);
_ <- modify(cx, |s| s + d * n);
s <- get(cx);
_ <- tell(cx, format!("s {s}"));
yield s
}.await
}
}
let cx = Cx(AskWith(2i64), Cx(StateCell::new(1i64), Cx(Collect::<String>::new(), End)));
assert_eq!(run(counted(&cx, 10)), Ok(21));
okay::effect! { pub enum Choose: Plain { Among(n: usize) -> usize = among; } }
okay::ops! { pub enum ChooseOps { Choose } } // the operations it may stop at: an enum, `From` each effect
#[resumable(ChooseOps)]
fn pick(base: usize) -> usize {
let a: usize = ask!(Choose::Among(3)); // a stop: the operation given to the handler
let b: usize = ask!(Choose::Among(2));
base + a * 10 + b
}
fn p() -> impl Prog<i64, eff![State<i64>, Choose]> {
Choose::among(vec![1, 2])
.flat_map(|x| State::get().flat_map(move |s: i64| State::put(s + x).map(move |_| s + x)))
}
Choose::all(State::run(0, p())).run(); // [(1, 1), (2, 2)]
State::run(0, Choose::all(p())).run(); // (3, [1, 3])
// An operation: a struct embedding Answers[R], R being what it answers.
type Ask struct{ okay.Answers[int] }
got := okay.Main(func(r *okay.Run) int {
return okay.Answer(r, func(Ask) int { return 1 }, func(outer okay.Cap[Ask]) int {
return okay.Answer(r, func(Ask) int { return 2 }, func(inner okay.Cap[Ask]) int {
return okay.Perform(outer, Ask{})*10 + okay.Perform(inner, Ask{})
})
})
}) // 12
effect Choose: many { among(n: i64) -> i64; }
effect Log { log(line: u8[:]); }
resumable fn pair() -> i64 ! Choose + Log {
let a = among(3);
log("a chosen");
let b = among(2);
return a * 10 + b;
}
handler all for Choose(out: i64[>..]&) {
return(v) { out.push(v); }
among(n, k) { for i in n { resume(k, i); } }
}
/// a plain function: its effects the context's, each operation a call — an effect missing, a compile error
fn greet(cx: anytype) usize {
ok.needs(@TypeOf(cx), .{ Name, Counter });
const who = cx.op(Name, .ask, {});
const n = cx.op(Counter, .get, {});
cx.op(Counter, .put, n + 1);
return who.len * 100 + n;
}
var cell: u32 = 7;
const cx = ok.context(.{ Counter.Cell{ .s = &cell }, Name.Given{ .env = "Ann" } });
_ = greet(cx); // 307
const Config = Reader("Config", of<number>())
const Counter = State("Counter", of<number>())
const program = eff(function* () {
const n = yield* Config.ask()
yield* Counter.modify((s) => s + n)
const s = yield* Counter.get()
return s * 2
})
// program: Eff<number, Reader<"Config", number> | State<"Counter", number>, boolean>
program.handle(Config.value(5)).handle(Counter.from(1)).run() // [6, 12]
@dataclass(frozen=True)
class Ask(Op[T], Generic[T]):
pass
def reader(v: T, body: Callable[[Cap[Ask[T]]], Eff[A]]) -> Eff[A]:
return (yield from answering(Ask, lambda _: v, body))
def prog(n: Cap[Ask[int]]) -> Eff[str]:
def body(s: Cap[Ask[str]]) -> Eff[str]:
return (yield from s.perform(Ask[str]())) * (yield from n.perform(Ask[int]()))
return (yield from reader("ab", body))
self.assertEqual(run(reader(3, prog)), "ababab")
-- in any row that has these three, at any place
def greet [In (Reader String) R] [In (Writer String) R] [In (State Nat) R] : Nat ! R := do
let name : String ← ask
tell s!"hello, {name}"
let n : Nat ← get
put (n + name.length)
pure n
#eval run (writer String (state 10 (reader "Ann" (greet (R := [Reader String, State Nat, Writer String])))))
-- ((10, 13), ["hello, Ann"])
Each form follows from what the language has: a continuation that can be copied or not, types that can hold a set of effects or not, a compiler that can be extended or not.
Java has no higher kinds and no type-level lists. Three answers are in the family.
okay-direct keeps no row at all: a program is plain code, an effect is a sealed interface whose
operations are records, a handler is an exhaustive switch, and the operation is the witness of its
answer's type. What can be resumed depends on the JVM: HotSpot's continuation and a virtual thread are resumed
once; Espresso can copy a suspended continuation's frames, so only EspressoEffects is a
MultiEffects, and Choose.all does not compile against the other two.
okay-java puts the handlers in force into a type variable: a handler's body is a generic method
at a level M extends L that nothing outside can name, so a capability that leaves its handler does
not compile (LeakTest). The price is in its README: a lambda cannot implement a generic method, so
every handler's body is an anonymous class or a method reference. Its second notation, okay.rows,
is the interface of okay-effects' Java kernel, below, on okay-java's own machine (docs/rows.md).
The Java kernel of okay-effects writes the row as a nested type with wildcards. It is
positional: a program names its effects in the order of its handlers and is moved with up,
under and fit; a handler takes its effect from the front of the row only.
Context parameters carry the effects:
context(accounts: Accounts, fail: Fail<String>) on a suspend function is checked by the compiler.
A handler that takes the rest of the body is a delimited
continuation on the standard library's coroutines. A Kotlin continuation is resumed once; k.copy()
copies the objects the compiler made of the suspend functions, up to the handler. That works on the JVM and in
JavaScript; native code has no way to copy a frame, and the multi-shot tests are in a source set for the JVM and
JS only. Handlers are chosen lexically: an operation goes to the handler whose capability it was given.
The data style is the other answer: k is data on a machine, on every platform.
An async block is a state machine the compiler builds, generic over its context and
monomorphized for the handlers it runs under; an operation answered in place is an inlined call. A future cannot
be cloned, so the crate okay is one-shot by design. Where the rest must be taken many times, the
rest has to be a value that is Clone: #[resumable] turns a function into an enum, one
variant for each stop holding the variables in scope there, on stable Rust; with the feature
coroutines, on nightly, the same comes from a coroutine that is cloned. Ownership does part of the
typing: a state whose type changes from step to step is a token each step takes and gives back, and a context
is a borrow that cannot outlive its handlers. In the Rust kernel of okay-effects a closure given to
flat_map is FnOnce + Clone, a recursive program ends with .boxed(), and
everything is of one thread.
Go has no type-level lists, so a program's effects cannot be in its type; they are its
capabilities, parameters and fields, as a Go program passes ctx. A handler answers only its own
capability's operations, so two instances of one effect coexist. A program is a coroutine
(iter.Pull); it cannot be copied, so a continuation is resumed once. Parallel branches run on
goroutines, and a branch performs through a capability only after it is lent to it; its operations travel over a
channel to the goroutine that owns the handler. Multi-shot is a separate package of programs as data.
Goose has no heap, no garbage collector, and function values only at compile time. So a set of
effects is a context's type, a handler is an overload of the operations for that type, and an interceptor is a
generic wrapper of a context. okay-goose is a translator, okgc, written in Goose:
it reads Goose with four additions (effect, effects, A ! F + G,
forward) and writes plain Goose on the same lines. A resumable fn is cut at each
operation of a once or many effect into a state machine whose frame is one struct.
A handler dropping the rest is return E from f, a jump.
Zig passes what a function needs as a value, as it does Allocator. A context is a
tuple of handlers known at compile time, and an operation is a call the compiler inlines. A type is a call the
compiler evaluates, so a row is Row(.{ F, G }) and a missing effect is a compile error with its own
message. A guard that may refuse an operation makes that effect's operations answer an error union, so the
program says try and the types show which operations may be refused. The machine takes steps
in place of closures: each a function declared once and named.
A union is copied by value, which is what a resumable program needs; the
states are written by hand.
A union type is a row: an effect is a tag with a name, and run() is present only
when the union is empty. A generator is the idiom, and a generator's state cannot be copied, so the third
parameter of Eff<A, E, boolean> records whether a program may be resumed many times. Types
are erased: the guarantees hold for code without casts, and at run time a generator resumed twice fails with
ResumedTwice.
Python has no rows. A program is a generator, typed by what each operation answers under
mypy --strict; its effects are the capabilities it is given. A generator cannot be copied, so a
program that a handler resumes many times is written as data, as in TypeScript; one machine runs both. The
business code stays a generator under asyncio: run_async awaits at the top.
An effect is an inductive family indexed by the answer's type, so dependent pattern matching
tells a handler that an operation is its own, with no cast. The monad laws and what each standard handler makes
of its operations are theorems; there is no sorry. The limit is stated in the README: the theorems
are about each interpreter's reference definition, and what runs is a second definition, unsafe and
not proved equal to the first.
| Stack | Waiting on the world | Control and audit of effects | |
|---|---|---|---|
| okay-java | An explicit stack on the heap; the tests run under -Xss512k |
Async: CompletableFuture, virtual threads |
In okay.rows, with okay-effects' Java names: Audit.audit with an
Interceptor, K.after, Effects.observe, Observer.observe
of every effect, std.Trail; a sensitive effect only under its audit. A program of the capability
notation gets them through the Bridge |
| okay-direct | Constant for handlers that answer in place | Async: structured concurrency, a virtual clock, channels |
Interceptor, Audited, Observer |
| okay-effects, Java and Rust | Constant: one loop, frames on the heap | Async; in Rust time is a queue the caller runs |
intercept, observe, an audit trail, k.after; a sensitive effect
only under its audit, refused by javac and rustc anywhere else |
| okay-kotlin | Constant for deep handlers (its README) | Runs inside kotlinx.coroutines as any suspend code | intercepted, audit with a Vault; a KSP generator |
| okay-rust | tail_rec for loops; the machine's stack is on the heap |
Step::Wait: a future, on any executor |
Audited, Policies, observe; a sensitive effect only under its
audit |
| okay-go | Go's own; prog: a million binds in constant stack |
All, Par, Race on goroutines |
Middleware over a program handed out to a host |
| okay-goose | A resume last in a clause is a loop |
— | A wrapper of a context with forward; sensitive effects are not there yet |
| okay-zig | The machine frees each node as it is read and reuses frames | On the wire, a direct-style call is a task of the worker's Io |
cx.observe, cx.guard |
| okay-ts | Constant: a million mutual calls | wait(promise), runPromise, cancellation |
audit, intercept, observe; a sensitive effect only under its
audit |
| okay-python | Data programs: 100 000 binds at Python's default recursion limit (its tests) | run_async, all_of, race on asyncio |
Middleware over a program handed out to a host |
| okay-lean | 108 operations on an 8 MB stack | — | intercept, around, observe, an audit trail; a sensitive effect only
under its audit, a program reaching it otherwise refused by Lean. Laws proved: a passing interceptor is none, an
observer is unseen, the trail of one path is its operations in order |
Speed. Three repositories measure the same lane, every path of a choice among 1000, on a rest
compiled to a state machine: okay-rust 0.28 µs a run (resumable/benches/lanes.rs, medians of three
rounds, 28 Oct 2026), okay-zig 0.38 µs (zig build lanes -Doptimize=ReleaseFast), okay-goose
0.40 µs (bench/results.md, medians of 3–5 rounds, 28 Oct 2026). Each number is its repository's own
measurement, from its own run; they were not measured together. The other repositories keep their own tables in
their READMEs.
One number is shared by four of the repositories on this page. Other suites are carried from repository to repository by hand. Some implementations are held to neither.
The TCK is in okay-kernel (tck/Tck.scala): a thousand random programs, with
handlers of logs, states, failures and choices nested in any order and each effect under two identities. Their
outcomes are folded into one number, -8623788747993822161, the digest of okay-kernel's reference
machine.
| Comes to the digest | Where | What is held to it |
|---|---|---|
| okay-java | okay-kernel, integration: TestTckOnJava; okay-java,
src/test/java/okay/rows/Tck.java |
okay-java as an implementation of okay-kernel's Java API (okay.core.Kernel). The TCK runs
there on okay-core, okay-java and the reference machine in one process, each nesting through the
others. And the row notation, okay.rows, by okay-effects' Java TCK, copied. |
| okay-rust | machine/tests/tck.rs |
The crate okay-machine. Not the crate okay, and not
okay-resumable. |
| okay-zig | test/tck_test.zig |
The machine. Not the contexts. |
| okay-effects | java/test/…/Tck.java, rust/tests/tck.rs, and the Scala kernel's |
Each of the three kernels. |
The ports generate the programs themselves and compare with the constant; the reference machine is in okay-kernel only. okay-direct, okay-kotlin, okay-go, okay-goose, okay-ts, okay-python and okay-lean do not run this TCK. okay-ts lists it as its next step.
| Held to | |
|---|---|
| okay-rust | okay-core's suites, each test named after the one it ports (tests/std.rs is TestStd,
tests/acceptance.rs TestAcceptance, tests/intercept.rs TestIntercept), and a
reference interpreter on random programs. These are the machine's. The crate okay runs the
interception programs again and compares the trails (tests/typed_intercept.rs). |
| okay-ts | The same three suites of okay-core, the same programs and values (test/std.test.ts,
test/acceptance.test.ts, test/intercept.test.ts). |
| okay-effects | The promises of its README as tests, named alike in each language (Properties), and
programs that must not compile, in Java, Rust and Scala. |
| okay-direct | Its own TCK, the artifact okay-direct-tck: all three implementations pass it, Espresso the
multi-shot one too. |
| okay-kotlin | Its own: EffectsTck and RenamingTck hold both engines of the data style to the
same answers. SpecTest runs the calculi of Forster, Kammar, Lindley and Pretnar (ICFP 2017) as
written there. |
| okay-java | In okay.rows: okay-effects' Java Properties and Audits, and its
programs that javac must refuse, copied with only their package changed (PropertiesTest,
NegativeTest). |
| okay-lean | Proofs of the laws. Its specification is okay-simple's Free; its tests of interception run
okay-simple's interception programs (Tests/InterceptTests.lean). |
| okay-go, okay-python, okay-goose | Their own tests. okay-goose compares the output of its examples with files kept beside them
(tests/run.sh). |
So the direct-style implementations are not held to the digest: it is a test of programs as data. And nothing shared checks okay-go, okay-python or okay-goose except what they do on a wire with another language, below.
In one process, through okay-kernel's protocol. Between processes, on the okay wire.
okay-kernel is a protocol with its API
in plain Java, no dependency: a program handed out steps to Done, Yield (an operation
and its k, multi-shot), Failed or Cancelled. okay-java implements it with
no wrapper: an Eff is the protocol's program, so its programs run under okay-core's handlers and
okay-core's under its. okay-ts has the same steps, and okay-kernel's module js exports the protocol
from Scala.js; test/kernel.test.ts runs programs both ways, and is skipped where that module is not
built. okay-go, okay-python and the crate okay of okay-rust hand a program out to a host in the same
shape (step, resume, fail, cancel), each in its own language.
The wire is the protocol of the okay
platform: a far side (a worker) serves functions and programs by name, a host asks and answers the programs'
operations under its own handlers, and may continue the same rest many times. Its specification is a transcript,
okay-py/src/main/resources/okay/py/wire.txt. The platform has the host and its own far-side
libraries for Python, R, TypeScript, Go, Rust and Haskell; Clojure, Frege and Java programs are walked inside
the JVM (docs/foreign-languages.md).
| Far side | Host | Checked by, in its own repository | |
|---|---|---|---|
| okay-effects | Yes, in Scala, Java and Rust | Yes, in all three | Every worker against six transcripts, every host with every worker: nine pairs. A part of the wire;
wire/README.md lists what is left out. |
| okay-zig | Yes: a child's pipes, or the host's process through a C ABI | No | okay's WireConformance, in full over pipes (its README;
conformance/TestZigLinks.scala) |
| okay-lean | Yes: pipes, or the JVM's process through FFM | Yes | okay's WireConformance over pipes and FFM (conformance/TestLeanLinks.scala).
Not served: direct style, streams, CBOR and compression. |
| okay-ts | Yes: pipes, TCP, a WebSocket | No | test/wire.test.ts: the transcript's requests and answers, multi-shot, a condition, over
TCP |
| okay-rust, okay-go, okay-python | No transport of their own. An operation crosses as its name and a record of its fields, through okay's worker for that language; a handler of theirs can answer the other side's operations. | One test each against okay's JVM host: examples/okayhost/TestOkayRustWire.scala,
TestOkayGoWire.scala, TestOkayPythonWire.scala | |
| okay-goose | Lines of JSON on standard input and output, with okay-rust on the other side: a Goose program answered by Rust handlers, a resumable function's stops answered by Rust, and a Rust program answered by Goose. | hosts/rust, tests/wire | |
| okay-java, okay-direct, okay-kotlin | No | No | — |
okay-effects keeps a script, wire/family/check.sh, that runs its host and workers
against the sibling repositories. Its README records one verification, on 10 November 2026, run twice:
130 passed, 0 failed, 4 skipped. What it records as checked:
| Host | Far side | What |
|---|---|---|
| okay-effects, Scala | okay-zig's worker | okay's conformance module: multi-shot, direct style, a condition, a held value, mux, streams under credit, an early stop, a duplex stream of 20 000 rows |
| okay-effects, Scala | okay-lean's worker | What Lean serves of the same module: multi-shot, a condition, a held value |
| okay-effects, Scala | okay-ts, its own serveStdio |
Multi-shot, a program as a generator, a condition |
| okay-effects, Scala | okay's reference Python shim | In JSON, CBOR and both with DEFLATE: calls, held objects, multi-shot, direct style, a condition; and
wire.txt, every line as written |
okay's wire.txt, line by line | okay-effects' Scala, Java and Rust workers | Every section of the transcript but the table call |
| okay's own host | okay-effects' Scala, Java and Rust workers | okay-effects' demonstration module: calls, held objects, multi-shot, mux, streams, a cancel, CBOR and DEFLATE, auth |
The same README lists what was not verified: okay's WireConformance suite on
okay-effects' workers; okay's Rust crate as a far side (one of the four skips); okay's Go, Haskell and R far
sides; okay-effects' Java and Rust hosts with the family's far sides; other machines than one, macOS on arm64.
okay-rust, okay-go, okay-python and okay-goose are not part of that check.
| Repository | Start with | How to add it |
|---|---|---|
| okay-java | Guide, okay-java as an implementation of okay-kernel | From source |
| okay-direct | Getting started, the implementations | Libraries: okay-direct |
| okay-kotlin | README, design | Libraries: okay-kotlin |
| okay-rust | README, the machine | From source |
| okay-go | Guide | go get github.com/okay-effects/okay-go |
| okay-goose | README, the tour | From source |
| okay-zig | README, design | zig fetch --save=okay "git+https://github.com/okay-effects/okay-zig#v0.1.0" |
| okay-ts | README | From source |
| okay-python | Guide | From source |
| okay-lean | README | From source |
| okay-effects | README, SPEC.md, the wire, the wire against the family | From source |
| okay-kernel | Design | From source |
| okay | Foreign languages | From source |