For a new program in Scala 3, use okay-scala. If the code must also compile on Scala 2.13, or it uses the libraries written over the common interface (okay-http, okay-mcp, okay-faces, okay-store), use okay-simple; its adapter runs the same code on okay-scala's machine. The other repositories are the platform and its kernel, a direct-style library for Java with Scala front ends, a reference model in three languages, and the experiments that came before them.
The state of the master branches on 11 November 2026. Every statement on this page
was checked in the repositories; where a file says it, the file is named. Outside Scala: effects in other languages.
Eleven repositories hold an implementation of effects in Scala. Seven are public. Three of them are released: okay-scala, okay-simple and okay-direct.
| Repository | What it is | How a program runs | Scala, platforms | Status |
|---|---|---|---|---|
| okay-scala | A library for Scala 3 alone. Package okay.scala3. No dependency. |
One CEK machine: a program is data (Prog), a step is one case of a @tailrec
loop over frames and handlers' levels. |
Built on Scala 3.3 LTS, tested on 3.9 too. JVM, Scala.js, Scala Native. | Released, 0.1.1. Last commit 11 Nov 2026. |
| okay-simple | One interface, Effectful, for Scala 3 and Scala 2.13, and implementations under it.
Package okay.effects, artifacts okay-effects-*. |
Three implementations: a machine with an explicit stack of frames and levels; the Free
monad, each handler its interpreter; tagless, a program made on whichever implementation runs it.
Adapters to other machines: see the bridges. |
Built on Scala 3.3 LTS and 2.13. JVM, Scala.js, Scala Native. | Released, 0.1.1. Last commit 4 Nov 2026. |
| okay-direct | Direct style for Java 25, with front ends for Scala 3 (okay.direct.scala3) and Scala 2.13
(okay.direct.scala2). |
No monad: perform is a method call, a handler gives its body a capability. Three
implementations: Loom's continuations, virtual threads, Espresso's continuations. |
Scala 3.9 and 2.13 over the Java API. JVM. | Released, 0.1.0. Last commit 23 Oct 2026. |
| okay-effects | The reference model: one small kernel written three times, in Scala 3, Java 25 and Rust. The Scala one is
the package okay.effects.scala. |
A program is data; one loop over a stack of levels, one level a handler. The model is the repository's
SPEC.md. |
Scala 3.3, built by scala-cli. JVM. | Public. Nothing is published as an artifact. Last commit 11 Nov 2026. |
| okay-core | The kernel of the okay platform: effects and delimited control. Packages okay.one (the
kernel), okay.core (the facade), okay (the words). |
One monad, Cont[R, I, O, A], on a CEK machine; a handler's k is data. |
Scala 3.9; modules for Scala 2.13. JVM, Scala.js, Scala Native. | Public. No release. Last commit 18 Oct 2026. |
| okay-kernel | A protocol between implementations, its API in plain Java; a reference machine in Scala,
Steps; and the family's TCK. |
A program steps to Done, Yield, Failed or Cancelled.
The reference machine is a stepper over an explicit stack of frames, "exact rather than fast". |
Scala 3.9 and 2.13. JVM, Scala.js, Scala Native. | Public. No release. Last commit 18 Oct 2026. |
| okay | The platform: over a hundred modules on effects. | On okay-core since 14 Oct 2026: a program A ! R is okay-core's own
(docs/contract.md). |
Scala 3.9; a build for Scala 2.13. JVM, Scala.js, Scala Native. | Public. Last commit 23 Oct 2026. |
| okay-free, okay-eff, okay-cont, okay-one |
The experiments of October 2026 that led to okay-core's one monad. | Effects on shift0 and $ in one monad.
See how they came about. |
Scala 3.9. | History. Not published. |
One name means three things. okay-effects is the organization. It is also the
name of okay-simple's artifacts and package (okay-effects, okay-effects-machine,
okay.effects). And it is the repository of the reference model, whose Scala package is
okay.effects.scala. On this page "okay-effects" is the repository.
Copied from each repository's own documentation.
import okay.scala3.* // the rows, programs, handlers, the machine
import okay.scala3.std.* // the standard effects: State, Reader, Writer, Fail, Async, ...
def greet[R[_]: Has[Reader % String] : Has[State % Int] : Has[Writer % String]]: Int ! R =
for
name <- Reader.ask
n <- State.get
_ <- State.put(n + 1)
_ <- Writer.tell(s"hello, $name")
yield n
type Greeting[X] = (Reader % String + State % Int + Writer % String)[X]
run(State.run(12)(Reader.run("Ann")(Writer.run(greet[Greeting])))) // (13, (List(hello, Ann), 12))
import okay.effects.*
import okay.effects.std.*
/** greets, in any row that has a `Reader` of the name and a `State` of the count */
def greet[R[_]: {Has[Reader % String], Has[State % Int]}]: String ! R =
for
name <- ask[String, R]
n <- get[Int, R]
_ <- put[Int, R](n + 1)
yield s"hello, $name (#${n + 1})"
type App = Reader % String + State % Int
val handled: (Int, String) ! Pure = state(0)(reader("Ann")(greet[App]))
val result: (Int, String) = run(handled)
In Scala 2.13 the same program takes its evidence as implicits, and a row is written with names:
((Reader % String) + (State % Int))#L.
import okay.effects.scala.*, std.* val p: Int ! State % Int + Choose = for x <- Choose.among(1, 2); s <- State.get[Int]; _ <- State.put(s + x) yield s + x run(Choose.all(State.run(0)(p))) // List((1, 1), (2, 2)) run(State.run(0)(Choose.all(p))) // (3, List(1, 3))
import okay.direct.{Effect, Effects}
import okay.direct.scala3.*
def program(heads: Boolean)(using Effect[Reader[Int]], Effect[State[Int]], Effect[Fail[String]]): Int =
val base = Reader.ask[Int]
State.put(State.get[Int] + 1)
val result = if heads then base else -base
if result < 0 then Fail.raise(s"negative: $result") else result
given Effects = LoomEffects()
Reader.run(42)(State.run(0)(Fail.attempt(program(true)))) // (1, Right(42))
import okay.*
def greet: Int ! Reader % String + Writer % String = // a program as its type says it
ask[String].flatMap(name => tell(s"hello, $name").map(_ => name.length))
run(reader("Ann")(writer[String, Int, Reader % String +: Pure](greet))) // (List("hello, Ann"), 3)
| Control and audit of effects | A continuation resumed many times | Constant stack | Checked by | |
|---|---|---|---|---|
| okay-scala | intercept, observe, around |
Yes | Yes: the run is one loop; defer and loop for tail calls |
Its own tests (157 on the JVM, 155 on Scala.js and Scala Native); okay-simple's interface suite, through the adapter |
| okay-simple | intercept, observe |
Yes | Yes | Its interface suite (okay-effects-tck), on every implementation |
| okay-direct | Interceptor, Audited, Observer |
On Espresso only; on Loom and on threads a continuation is resumed once | For handlers that answer in place; a general handler nests the stack at each resumption | Its own TCK (okay-direct-tck, JUnit) |
| okay-effects | intercept, observe, around (Scala), an audit trail; an effect
marked sensitive is handled only under its audit (in Scala, Java and Rust) |
Yes (in Scala, Java and Rust) | Yes | Its properties, the same in three languages (Scala 118 tests; Java 64 properties, 27 wire tests, 8 programs refused by javac; Rust 125 tests); the family's TCK digest; the wire's transcripts |
| okay-core | audit, observe, intercept; an effect marked sensitive is handled
only under its audit |
Yes | Yes | Its reference in CPS (TestReference); the family's TCK digest; okay-simple's interface
suite |
| okay-kernel | — | Yes: the protocol's k is multi-shot |
— | The family's TCK is here; its reference machine gives the digest |
In notation, mostly. In classes, no: each library has its own, and a program of one runs on another only through an adapter. In behaviour, as far as two shared test suites reach. On the wire, one of them.
okay-simple, okay-scala and the Scala kernel of okay-effects write a program the same
way. A ! R is a program of the value A over the row R. A row is a
type constructor: R + E is [X] =>> Sum[R, E, X], F % S is the effect
F with its parameter, Pure is the empty row. In[E, R] and
Has[E] say that an effect is in a row, Split[E, R, Rest] is what a handler takes,
Sub[R, S] puts a program into a bigger row. All three define these names.
They are three sets of classes. Sum, Void and the program type are
defined in each library, in okay.effects, okay.scala3 and
okay.effects.scala. A program of one is not a program of another, and a handler of one does not take
a program of another.
The words differ. In okay-simple an operation is a function that names its row,
get[Int, R], and a handler is state(0). In okay-scala and in okay-effects an operation
is a word of its effect's object, State.get, and a handler is State.run(0). okay-scala
names two effects Fail and Yield; okay-effects names them Throws and
Emit and has the other two names as aliases.
okay-core and okay use the same three symbols, A ! E1 + E2 and
F % S, over a different row: a type, E +: T ending in Pure, with a typed
path to each effect.
okay-direct has no row and no monad. A program is plain code that takes a
capability for each effect, using Effect[State[Int]]; a body with no handler for an effect does
not compile.
Every adapter starts from okay-simple's interface. None runs a program of okay-scala, okay-direct or okay-effects on another implementation.
| From | To | Where | What it does |
|---|---|---|---|
| okay-simple | okay-scala | okay-simple, module cek: okay-effects-cek, released, Scala 3 |
OnCek runs okay-simple's interface on okay-scala's machine. OnCek.into[S](p)
puts such a program into a row of an okay-scala program: one machine, one run. No class is shared. |
| okay-simple | okay-core | okay-core, module effects: okay-effects-core |
Core: a program of the interface is okay-core's own Cont. |
| okay-simple | okay-kernel | okay-kernel, module effects: okay-effects-kernel, Scala 3 and 2.13 |
OnKernel.on(effects): the interface on any implementation of the protocol. |
| okay-simple | okay-direct | okay-simple, core/direct: okay-effects-direct, JVM; in the repository, not in
the release 0.1.1 |
OnDirect: the interface's programs run by okay-direct's handlers. |
| okay-core | okay-kernel | okay-core: okay.core.kernel.OnCore |
The protocol on the one monad. |
okay-simple, okay-scala and okay-direct each have interop modules with cats-effect and ZIO; okay-simple also with fs2, monix, kyo and tofu, okay-scala with tofu.
Two test suites are shared between repositories. Neither covers all of them.
The family's TCK is in okay-kernel (tck/Tck.scala): a thousand random
programs, their outcomes folded into one number, -8623788747993822161. These come to that number:
okay-kernel's reference machine, okay-core through OnCore, okay-java (run from okay-kernel's
integration tests), the machine crate of okay-rust, the machine of okay-zig, and each of the three kernels of
okay-effects. okay-effects compares with the constant; the reference machine is not in that repository
(its README says so). okay-simple, okay-scala and okay-direct do not run this TCK. The okay platform does not
run it itself; it runs on okay-core, which does.
okay-simple's interface suite (InterfaceSuite, the artifact
okay-effects-tck) runs on the machine, on Free, on tagless over both, on okay-scala's
machine through OnCek, on okay-core through Core, and on okay-kernel's reference machine
through OnKernel. On okay-direct it runs through OnDirect; on Loom and on threads the
tests that resume a continuation twice are refused.
So okay-scala and okay-effects are not held to one test: okay-scala is not checked by the digest, and okay-effects is not checked by the interface suite.
The okay wire is the protocol of the okay platform: a program runs in one process and its
operations are answered in another. Of the other repositories on this page only okay-effects speaks it, in all
three languages. It implements a part: its wire/README.md lists what is left out (tables and data
frames, TCP and TLS among them). Its hosts and workers are checked with each other and against transcripts; a
script, wire/family/check.sh, runs its host with the far sides of okay-zig, okay-lean, okay-ts and
okay's Python shim, and okay's own host with its three workers, where those repositories are at hand. okay-simple, okay-scala, okay-direct and
okay-core have no implementation of the wire.
The two closest: the same notation, a machine of frames and levels in both, the same five
handler shapes.
okay-scala as released in 0.1.1; okay-effects' Scala kernel as on its master.
| okay-scala | okay-effects, the Scala kernel | |
|---|---|---|
| Purpose | A library to write applications on. | A reference: the model in its smallest form, the same in Java 25 and Rust, with a specification
(SPEC.md). |
| Platforms | JVM, Scala.js, Scala Native. Built by sbt. | JVM. Built by scala-cli. |
| Release | 0.1.1, a Maven repository on GitHub Pages. Binary compatibility with the release is checked by MiMa. On this site, okay-serverless and okay-proof resolve it. | Nothing is published; it is used from source. |
| Standard effects | State, Reader, Writer, Fail, Abort, Choose, Yield, Control, Lifted, Fresh, Random, Store, Clock, Env,
Log, Async with fibers, Sync, Scope; and Ref, Deferred, Semaphore,
Queue, Stream, Retry, Coeffects. |
Nine: State, Reader, Writer, Throws, Choose, Fresh, Clock, Emit (streams), Async. |
| Interop | cats-effect, ZIO, tofu. | None. |
| Casts | No asInstanceOf in the main sources. 34 matches are marked @unchecked. |
No asInstanceOf and no @unchecked, in the sources and in the tests. |
| Words | Both: a word such as State.get names no row; it takes the row of the program's
declared type, or of an evidence in scope. In okay-effects since 10 Nov 2026; in okay-scala since the 0.1.1
published on 11 Nov 2026. | |
| The row a handler leaves | Both: the row as one would write it. State % Int + Choose without the state is
Choose. | |
| Handler shapes | Both: general, tail, abort, stateful, and
control: k as a value, a Continuation, that is resumed or unwound
(k.unwind). | |
ensuring |
The finalizer is a program of the row, or a thunk, run by the machine. Leaving is an operation:
k.unwind runs the finalizers waiting in k. Every standard handler that drops a rest
unwinds it: Fail, Abort, Choose at a choice of nothing,
Yield.take at its cut, Lift at a None or a Left,
Async at a cancel, a race's loser, a timeout. Scope releases by
ensuring, and so does a stream's pull that is left. A k neither resumed nor unwound
runs none. |
The finalizer is a program, run by the machine. Leaving is an operation: k.unwind runs the
finalizers waiting in k. A k neither resumed nor unwound runs none. |
| A program of a smaller row in a bigger one | Both: it runs under one more level of the machine, which renames its operations on their way out, and embeddings compose: one entered in tail position on the level of another takes no level of its own, so calls in tail position between rows keep one level. | |
| Control and audit | intercept, observe, around. No sensitive effects. |
The same, and k.after; an audit trail, Audit; an effect declared sensitive is
handled only under its audit (audit, handleAudited), and the compiler refuses it
anywhere else. |
| The wire | No. | Yes, a part of it: scala/src/wire. |
| Tests in common | okay-simple's interface suite, through the adapter. | The family's TCK digest. |
Dates are those of the commits and tags in each repository.
| When | What |
|---|---|
| 2 Jul 2025 | The first commit of okay, the platform. |
| 7 Oct 2026 | okay-core begins: delimited continuations over rows of effects. |
| 10 Oct 2026 | okay-kernel begins: one API for every language and platform. |
| 12–14 Oct 2026 | The experiments beside okay-core: okay-free, okay-eff, okay-cont, okay-one.
Each tried one way of putting effects and delimited control in one monad. okay-free kept the answer types in
the effects' monad. okay-eff took them out and made k data. okay-cont put the rows inside the
monad of shift0 and $. okay-one made the boundary between the framework and its
kernel data. They are not published; what each found is recorded in okay-core's
docs/one.md. |
| 13–14 Oct 2026 | The decision: okay-core is one monad on one machine, k is data.
okay-one's kernel becomes okay-core's package okay.one, and the okay platform moves onto
it. |
| 17–18 Oct 2026 | okay-simple: one interface for Scala 3 and 2.13. Released as 0.1.0 on 18 Oct; 0.1.1 on 4 Nov. |
| 22–23 Oct 2026 | okay-direct: direct style on Java 25, with the Scala front ends. Released as 0.1.0. |
| 23 Oct 2026 | okay-scala: Scala 3 alone, one CEK machine. Released as 0.1.0 the same day. |
| 10 Nov 2026 | okay-effects, the repository: the kernel in Scala 3, Java 25 and Rust. Words that take their row from the declared type, and the row a handler leaves, are committed there, and in okay-scala later the same day. |
| 11 Nov 2026 | okay-scala 0.1.1 is published with them. |
Two lines are developed for Scala today: okay-scala, for applications in Scala 3, and okay-simple, the common interface for Scala 2.13 and 3. okay-effects is where the model is kept small and checked in three languages. okay-core is the kernel under the okay platform.
| Repository | Start with | How to add it |
|---|---|---|
| okay-scala | Getting started, design | Libraries: okay-scala |
| okay-simple | Getting started, the implementations, the family | Libraries: okay-simple |
| okay-direct | Scala: the front ends and the interop | Libraries: okay-direct |
| okay-effects | README, SPEC.md, the wire | From source |
| okay-core | Guide, control and audit of effects | From source |
| okay-kernel | Design | From source |
| okay | The contract in three parts | From source |