Type-Safe Ownership
Values are tagged with their owning location at the type level. The compiler prevents unauthorized access, no runtime checks needed.
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 processfor backend <- Backend.local(List(alice, bob)) aliceIO = greeting.project(backend, alice) bobIO = greeting.project(backend, bob) _ <- (aliceIO, bobIO).parTupledyield ()
// Over TCP — each participant on its own connectionTcpBackend.local[IO](List(alice, bob)).use: backend => val aliceIO = greeting.project(backend, alice) val bobIO = greeting.project(backend, bob) (aliceIO, bobIO).parTupled.void