我想开始我的项目中使用免费的单子,我挣扎,使其优雅。
比方说,我有两个上下文(在现实中我有更多) - Receipt
和User
- 都有一个数据库的操作,我想保持自己的独立口译员,并在最后时刻撰写他们。
为此,我需要为每个操作定义不同的操作,并使用Coproduct
将它们合并为一个类型。
这是我在Google上搜寻和阅读的天:斯卡拉免费单子与余积和单子转换
// Receipts
sealed trait ReceiptOp[A]
case class GetReceipt(id: String) extends ReceiptOp[Either[Error, ReceiptEntity]]
class ReceiptOps[F[_]](implicit I: Inject[ReceiptOp, F]) {
def getReceipt(id: String): Free[F, Either[Error, ReceiptEntity]] = Free.inject[ReceiptOp, F](GetReceipt(id))
}
object ReceiptOps {
implicit def receiptOps[F[_]](implicit I: Inject[ReceiptOp, F]): ReceiptOps[F] = new ReceiptOps[F]
}
// Users
sealed trait UserOp[A]
case class GetUser(id: String) extends UserOp[Either[Error, User]]
class UserOps[F[_]](implicit I: Inject[UserOp, F]) {
def getUser(id: String): Free[F, Either[Error, User]] = Free.inject[UserOp, F](GetUser(id))
}
object UserOps {
implicit def userOps[F[_]](implicit I: Inject[UserOp, F]): UserOps[F] = new UserOps[F]
}
当我想要写一个程序,我可以做到这一点:
type ReceiptsApp[A] = Coproduct[ReceiptOp, UserOp, A]
type Program[A] = Free[ReceiptsApp, A]
def program(implicit RO: ReceiptOps[ReceiptsApp], UO: UserOps[ReceiptsApp]): Program[String] = {
import RO._, UO._
for {
// would like to have 'User' type here
user <- getUser("user_id")
receipt <- getReceipt("test " + user.isLeft) // user type is `Either[Error, User]`
} yield "some result"
}
这里的问题是,例如user
在理解是Either[Error, User]
类型,看看getUser
签名是可以理解的。
我想有是User
型或停止计算。
我知道我需要以某种方式使用EitherT
单子转换或FreeT
,但尝试,我不知道如何结合的类型,使其工作小时后。
有人可以帮忙吗? 如果需要更多细节,请告诉我。
我还创建了一个最小的SBT项目在这里,所以任何人都愿意帮助可以运行它:https://github.com/Leonti/free-monad-experiment/blob/master/src/main/scala/example/FreeMonads.scala
干杯, Leonti
如果你不想处理的'Free'程序错误,只是定义'GetUser'为'案例类的getUser(ID:字符串)扩展UserOp [用户]'让解释器处理错误。类似于'GetReceipt'。 –
@TomasMikula,但我想处理程序内部的错误,我只是想自动完成。 请看看这篇文章: https://medium.com/iterators/free-monads-in-web-stack-part-i-2955d44757b5 这个家伙使用EitherT配有单子,所以当你有一个错误计算自动停止,无需解开任何一个。 – Leonti
是的,所以你想让翻译处理它;编写“免费”程序时,您不想处理错误。那篇文章有'Action'返回'Either',然后是解释器'Action〜> Id'。相反,它可以有'Action's只返回成功的结果,然后有一个解释器'Action〜>任一[Error,?]'。不需要'EitherT',至少不需要用户方。这也使得错误类型由解释器决定。 –