【发布时间】:2021-11-27 06:17:29
【问题描述】:
我正在尝试编写将实体保存到数据库的函数的Cats MTL 版本。我希望这个函数从环境中读取一些SaveOperation[F[_]],执行它并处理可能的失败。到目前为止,我想出了这个函数的 2 个版本:save 是更多态的 MTL 版本,save2 在其签名中使用精确的单子,这意味着我将自己限制在使用 IO。
type SaveOperation[F[_]] = Employee => F[Int]
def save[F[_] : Monad](employee: Employee)(implicit
A: Ask[F, SaveOperation[F]],
R: Raise[F, AppError]): F[Unit] =
for {
s <- A.ask
rows <- s(employee)
res <- if rows != 1 then R.raise(FailedInsertion)
else ().pure[F]
} yield res
def save2(employee: Employee): Kleisli[IO, SaveOperation[IO], Either[AppError, Unit]] =
Kleisli((saveOperation) => saveOperation(employee)
.handleErrorWith(err => IO.pure(Left(PersistenceError(err))))
.map(rows =>
if rows != 1 then Left(FailedInsertion)
else Right(())
)
)
我以后可以这样称呼:
val repo = new DoobieEmployeeRepository(xa)
val employee = Employee("john", "doe", Set())
type E[A] = Kleisli[IO, SaveOperation[IO], Either[AppError, A]]
println(EmployeeService.save[E](employee).run(repo.save).unsafeRunSync())
println(EmployeeService.save2(employee).run(repo.save).unsafeRunSync())
问题是save 的调用出现以下错误:
Could not find an instance of Monad for E.
I found:
cats.data.Kleisli.catsDataMonadErrorForKleisli[F, A, E]
But method catsDataMonadErrorForKleisli in class KleisliInstances0_5 does not match type cats.Monad[E].
这个错误对我来说似乎没有意义,因为两个函数的有效签名完全相同,所以单子应该在那里。我怀疑问题出在Ask[F, SaveOperation[F]] 参数上,因为这里的F 不是IO,而SaveOperation 需要IO。
为什么我不能使用 Kleisli monad 进行 save 调用?
更新:
如果我将类型修改为E[A] = EitherT[[X] =>> Kleisli[IO, SaveOperation[IO], X], AppError, A],我会收到一个新错误:
Could not find an implicit instance of Ask[E, SaveOperation[E]]
我猜 SaveOperation 的正确泛型类型应该是 IO,但我不知道如何通过 Ask 的实例正确提供它
【问题讨论】:
标签: scala functional-programming monads scala-cats cats-effect