Skip to content

Instantly share code, notes, and snippets.

@kenbot
Created May 1, 2017 03:49
Show Gist options
  • Save kenbot/dbe3cbab959b2c1e09c16f34328e3a6b to your computer and use it in GitHub Desktop.
Save kenbot/dbe3cbab959b2c1e09c16f34328e3a6b to your computer and use it in GitHub Desktop.
Example of Coyoneda use with free monads in Scala
import scalaz._, Scalaz._
// Model
case class UserId(id: String)
case class RequestId(id: String)
case class Message(body: String)
trait Authenticator { def authenticate(token: String): Option[UserId] }
trait MessageDb {
def getMessageList(id: String): List[String]
def addMessage(id: String, body: String): Unit
}
// DSL instructions
sealed trait AppAction[A]
case class Authenticate(token: String) extends AppAction[Option[UserId]]
case object GetRequestId extends AppAction[RequestId]
case class GetMessages(uid: UserId) extends AppAction[Seq[Message]]
case class AddMessage(uid: UserId, msg: Message) extends AppAction[Unit]
// User-facing monadic interface
object Scripts {
type ScriptImpl[A] = Coyoneda[AppAction, A]
type Script[A] = Free[ScriptImpl, A]
def authenticate(token: String): Script[Option[UserId]] = toScript(Authenticate(token))
def getRequestId: Script[RequestId] = toScript(GetRequestId)
def getMessages(uid: UserId): Script[Seq[Message]] = toScript(GetMessages(uid))
def addMessage(uid: UserId, msg: Message): Script[Unit] = toScript(AddMessage(uid, msg))
private def toScript[A](a: AppAction[A]): Script[A] = Free.liftFC[AppAction, A](a)
type Interpreter[F[_]] = AppAction ~> F
def interpret[A, F[_]: Monad](script: Script[A], interp: Interpreter[F]): F[A] = Free.runFC(script)(interp)
}
// Interpreter
class AppInterpreter(auth: Authenticator, rid: RequestId, db: MessageDb) extends Scripts.Interpreter[Id] {
def apply[A](action: AppAction[A]): A = action match {
case Authenticate(token) => auth.authenticate(token)
case GetRequestId => rid
case GetMessages(UserId(id)) => db.getMessageList(id).map(Message.apply)
case AddMessage(UserId(id), Message(body)) => db.addMessage(id, body)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment