A library for choreographic programming in Scala 3.
The implementation is based on the paper HasChor: Functional Choreographic Programming for All by Gan Shen, Shun Kashiwa and Lindsey Kuper, and its Haskell implementation.
Choreo uses Mill as its build tool.
# Compile
./mill __.compile
# Run tests
./mill core.test
# Run examples
./mill examples.runMain choreo.examples.kv
./mill examples.runMain choreo.examples.booksellerimport cats.effect.IO
import cats.effect.IO.asyncForIO
import cats.effect.kernel.Ref
import cats.syntax.all.*
import choreo.backend.Backend
type State = Map[String, String]
enum Request:
case Get(key: String)
case Put(key: String, value: String)
type Response = Option[String]
val client: "client" = "client"
val server: "server" = "server"
def main: IO[Unit] =
for
backend <- Backend.local(List(client, server))
clientTask = choreo.project(backend, client)
serverTask = choreo.project(backend, server)
_ <- (clientTask, serverTask).parTupled
yield ()
def choreo: Choreo[IO, Unit] =
for
stateS <- server.locally(Ref.of[IO, State](Map.empty))
_ <- step(stateS).foreverM
yield ()
def step(stateS: Ref[IO, State] @@ "server"): Choreo[IO, Unit] =
for
reqC <- client.locally(readRequest)
resC <- kvs(reqC, stateS)
_ <- client.locally:
resC.!.fold(IO.println("Key not found")):
IO.print("> ") *> IO.println(_)
yield ()
def kvs(
reqC: Request @@ "client",
stateS: Ref[IO, State] @@ "server"
): Choreo[IO, Response @@ "client"] =
for
reqS <- client.send(reqC).to(server)
resS <- server.locally(handleRequest(reqS.!, stateS.!))
resC <- server.send(resS).to(client)
yield resC
def handleRequest(
req: Request,
state: Ref[IO, State]
): IO[Response] =
req match
case Request.Get(key) =>
state.get.map(_.get(key))
case Request.Put(key, value) =>
state.update(_.updated(key, value)).as(Some(value))
// Requests are either:
// GET key
// PUT key value
def readRequest: IO[Request] =
for
_ <- IO.print("> ")
line <- IO.readLine
req <- line.split(" ") match
case Array("GET", key) =>
IO.pure(Request.Get(key))
case Array("PUT", key, value) =>
IO.pure(Request.Put(key, value))
case _ =>
IO.raiseError(new Exception("Invalid request"))
yield reqThis library is released under the same license as HasChor, namely the BSD-3 license.
Please see the LICENSE within this repository for the full text of the license.