Open-source project
getkyo/kyo avatar
getkyo/kyo

kyo: a Scala 3 toolkit where unhandled effects do not compile

Toolkit for Scala Development

822 stars126 forksScalaApache-2.0

At a glance

What is it?
Kyo gives Scala programmers algebraic effects with modular handlers, so an error path left unhandled is a compile error and an effect used without being declared is rejected at the call site. The cost is a steep learning curve, four required compiler flags, and a 1.0 release that is still on release candidate seven.
Who is it for?
Adopt kyo if you are writing Scala 3 and want the compiler to enforce that every side effect is handled, and you are willing to restructure existing code into that model. Do not adopt it for an ordinary CRUD service where a plain effect type will do, because the API surface, the four mandatory compiler flags and the 1.0 release candidate status all add cost that only pays off if you use the guarantees.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Scala, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The pending type is the whole API

Kyo is a Scala 3 toolkit for building applications, and its starting point is what it makes impossible. The README lists the guarantees directly: an error path left unhandled does not compile, an effect used without being declared in the signature is rejected at the call site, a resource cannot outlive its scope even when something fails partway through, concurrent work started together cannot leak its children, and a durable workflow's progress survives a crash and resumes from the last completed step. The mechanism is algebraic effects with modular handlers, exposed through a compact infix type `A < S`, read as A pending S. A is the value the computation produces and S is the open, type-level set of effects it needs. A function returning `A < (Sync & Abort[E] & Env[Config] & Emit[Log])` names its four effects in its return type. Handlers remove effects from the set one at a time, the call site decides where each one runs, and the compiler refuses to execute anything until the set is empty. Composition stays ordinary Scala: map, flatMap and for-comprehensions over plain values.

Effects are a type-level set, not a list of imports

The set `S` is a Scala 3 intersection type built from individual effect markers such as `Sync`, `Async`, `Abort[E]`, `Env[R]`, `Var[V]`, `Emit[V]` and `Scope`, and you can define your own. An effect is added to the row by mentioning it in the type and removed by applying its handler. The README shows the progression in three steps, where a bare 42 lifts into the empty row automatically through an implicit conversion, so there is no need for a pure or Kyo.lift call, and any non-computation expression in a position that wants a computation is lifted on the spot. Then an Abort effect joins the row, and then adding Sync wraps that in a side-effectful step so the row carries both. Handlers discharge one effect at a time, returning a value whose row no longer mentions that effect, so an Abort.run on an `Abort[String]` leaves a row of `< Sync`. The set discipline is the point. A function's signature is a complete inventory of what it does, and that inventory is checked by the compiler rather than by review.

Four compiler flags are mandatory, not optional

The README is unusually firm about the build configuration. These Scala compiler flags are required when working with Kyo, because they catch common mistakes, silently discarded effects and untyped equality, that the API surface relies on you not making. The first is -Wvalue-discard, which warns when non-Unit expression results are unused. The second is -Wnonunit-statement, which warns when non-Unit expressions are used in statement position. The third, -Wconf:msg=(unused.*value|discarded.*value|pure.*statement):error, elevates the warnings from the previous flags into compilation errors. The fourth is -language:strictEquality, which enforces type-safe equality by requiring explicit evidence that types can be safely compared. They go into your build.sbt like this:

scala
scalacOptions ++= Seq(
    "-Wvalue-discard",
    "-Wnonunit-statement",
    "-Wconf:msg=(unused.*value|discarded.*value|pure.*statement):error",
    "-language:strictEquality"
)

Read that as a design decision with a cost. Kyo cannot detect a discarded effect by type alone, so it uses compiler warnings to cover the gap, and then turns those warnings into errors so they cannot be missed. The strictEquality flag is a Scala 3 feature that will also affect code unrelated to effects, which is worth knowing before you enable it on an existing codebase.

Adding the modules to build.sbt

You add the modules you want to build.sbt, and most application code starts with kyo-core. The README gives the shape:

sbt
libraryDependencies ++= Seq(
    "io.getkyo" %% "kyo-core" % "<version>",
    "io.getkyo" %% "kyo-http" % "<version>"    // optional: HTTP client/server
)

The `%%` operator covers JVM and Scala Native, and `%%%` covers the Scala.js and WebAssembly backends, with a note to consult the module tables for per-module platform support. The version placeholder is a Maven Central badge, and the group is io.getkyo. A minimal application extends `KyoApp`, the entrypoint trait that discharges the effect row your main body produces:

scala
import kyo.*

object Hello extends KyoApp:
    run {
        Console.printLine("Hello from Kyo")
    }
end Hello

The README suggests a reading order: kyo-core/README.md first, covering Sync, Async, Scope, Fiber, KyoApp and the standard concurrent primitives, then kyo-http for the natural next step, then the module map as the application grows. One thing the README flags that will cost an afternoon if you skip it: Kyo uses advanced features from the latest Scala 3 versions that IntelliJ IDEA may not fully support, and the recommendation is a Metals-based IDE with the SBT BSP server, with a guide for switching from Bloop to SBT BSP.

One source tree, four compilation targets

The README states that one source tree compiles to JVM, JavaScript, Scala Native and WebAssembly, and the build.sbt in the repository root is where that is configured. The module directory listing shows the breadth of the ecosystem. There are over forty module directories in the tree, including kyo-core, kyo-http, kyo-config, kyo-crypto, kyo-data, kyo-logging-jpl and kyo-logging-slf4j, kyo-lsp, kyo-mcp, kyo-markdown, kyo-i18n, kyo-jsonrpc and kyo-jsonrpc-http, kyo-reactive-streams, kyo-actor, kyo-aeron, kyo-ai, kyo-browser, kyo-caliban, kyo-case-app, kyo-charset, kyo-combinators, kyo-compat, kyo-compiler, kyo-direct, kyo-discord, kyo-doctest, kyo-examples, kyo-ffi, kyo-flow and kyo-flow-conformance, kyo-kernel, kyo-mime, kyo-net, kyo-offheap, kyo-parse, kyo-pod, kyo-prelude, kyo-scheduler-finagle and kyo-scheduler-pekko. That is a large surface for a 1.0 release candidate, and the naming convention makes the shape guessable: transport, encoding, logging, protocol and scheduler modules all sit next to the core. The README also links a video on Suspension, the magic behind composability from Lambda Days 2025, and a migration table for people coming from ZIO or Cats Effect.

Where the model costs you

Three costs are visible in the documentation rather than hidden. The first is that an effect in the type is an obligation on every caller, so a function that uses Sync forces its callers to handle Sync, and a library you depend on that was not written for Kyo will not fit the row. That is the price of the guarantee and it is a real price, because it means the effect row is not only a local discipline but an interface property that propagates through your dependencies. The second is the tooling cost: the README recommends against IntelliJ IDEA for this project, which for most teams means changing how they build and navigate code. The third is the version maturity. The listed releases are v1.0.0-RC5 on 2026-07-01, v1.0.0-RC6 on 2026-08-05 and v1.0.0-RC7 on 2026-09-28, so the 1.0 line has been on release candidates for a while, and the repository is not archived with the last push on 2026-10-21. Adopting a release candidate library in production is a different decision from adopting a stable one, whatever the guarantees the type system offers.

The theory underneath, and who else does this

The README is explicit that the design is not original, and saying so is a good sign. The theory traces back to Plotkin and Pretnar and to languages like Koka and Eff. The distinguishing feature against those languages is the compact infix type and the fact that composition stays ordinary Scala rather than requiring its own language or its own syntax for effects. The same kind of guarantee exists in other Scala effect libraries, and the README anticipates the comparison directly with a migration table for people arriving from ZIO or Cats Effect. That table matters more than it sounds, because the differences between effect systems are not cosmetic. Where one library uses a wrapper type and a dedicated monad, Kyo uses an intersection type in the signature, which changes what an ordinary function signature looks like and therefore changes how a library written for one system can be consumed by the other. The other claim worth reading closely is that the durable workflow progress survives a crash and resumes from the last completed step, which is a stronger statement than most effect libraries make and one the repository backs with kyo-flow and kyo-flow-conformance modules.

Editorial conclusion

Adopt kyo if you are writing Scala 3 and want the compiler to enforce that every side effect is handled, and you are willing to restructure existing code into that model. Do not adopt it for an ordinary CRUD service where a plain effect type will do, because the API surface, the four mandatory compiler flags and the 1.0 release candidate status all add cost that only pays off if you use the guarantees. Before you commit, read kyo-core/README.md for Sync, Async, Scope, Fiber and KyoApp, check the migration table for ZIO or Cats Effect users, and pin an exact version since the newest release is v1.0.0-RC7.

Frequently asked questions

What is kyo?

It is an Apache-2.0 licensed Scala 3 toolkit for building applications, built on algebraic effects with modular handlers. A computation has the type A pending S, where A is the value produced and S is the type-level set of effects it needs, and the compiler refuses to run anything until every effect in the set has been handled.

How do I add kyo to an sbt build?

Add the modules you want to build.sbt, most often io.getkyo %% kyo-core, using %% for JVM and Scala Native and %%% for the Scala.js and WebAssembly backends. The README also requires four compiler flags: -Wvalue-discard, -Wnonunit-statement, -Wconf:msg=(unused.*value|discarded.*value|pure.*statement):error and -language:strictEquality.

Which Scala versions does kyo need?

Kyo uses advanced features from the latest Scala 3 versions that IntelliJ IDEA may not fully support, so the README recommends a Metals-based IDE with the SBT BSP server instead. The project is published for Scala 3 with a Scaladoc entry for the kyo-core_3 artifact on javadoc.io.

Is kyo ready for production use?

The newest listed releases are v1.0.0-RC5, v1.0.0-RC6 and v1.0.0-RC7, the last dated 2026-09-28, so the 1.0 line is still on release candidates. The repository is not archived and the last push was on 2026-10-21, so work is still landing.

Official sources

  1. getkyo/kyo on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/getkyo-kyo.svg)](https://hysenlabs.com/projects/getkyo-kyo)

Was this useful?