Skip to content

Choreo

Write one global protocol. Get correct distributed programs for free.

Type-Safe Ownership

Values are tagged with their owning location at the type level. The compiler prevents unauthorized access, no runtime checks needed.

Deadlock-Free by Construction

If your choreography compiles, the distributed system is guaranteed free of communication deadlocks. Correctness comes from the type system, not testing.

Automatic Endpoint Projection

Write the protocol once as a global description. Each participant’s network code is derived automatically via endpoint projection.

Scala 3 & Cats Effect

Built on Scala 3’s singleton types and for-comprehensions. Integrates naturally with cats-effect for effectful, concurrent execution.

1. Define locations: each participant is a singleton string type:

import choreo.*, choreo.backend.*
import cats.effect.IO, cats.syntax.all.*
val alice: "alice" = "alice"
val bob: "bob" = "bob"

2. Write the choreography: one global protocol, all participants in a single description:

def greeting: Choreo[IO, Unit] =
for
msgA <- alice.locally(IO.pure("Hello from Alice!"))
msgB <- alice.send(msgA).to(bob)
_ <- bob.locally(IO.println(s"Bob received: ${msgB.!}"))
yield ()

3. Run it: endpoint projection compiles the global protocol into per-participant network code.
Run in-process for testing, or over TCP for real deployment:

// In-memory — both participants in a single process
for
backend <- Backend.local(List(alice, bob))
aliceIO = greeting.project(backend, alice)
bobIO = greeting.project(backend, bob)
_ <- (aliceIO, bobIO).parTupled
yield ()
// Over TCP — each participant on its own connection
TcpBackend.local[IO](List(alice, bob)).use: backend =>
val aliceIO = greeting.project(backend, alice)
val bobIO = greeting.project(backend, bob)
(aliceIO, bobIO).parTupled.void