首页 > 解决方案 > 堆叠 monads Writer 和 OptionT

问题描述

我有以下代码:

override def getStandsByUser(email: String): Try[Seq[Stand]] =
  (for {
    user <- OptionT(userService.findOneByEmail(email)): Try[Option[User]]
    stands <- OptionT.liftF(standService.list()):[Try[List[Stand]]]
    filtered = stands.filter(stand => user.stands.contains(stand.id))
  } yield filtered).getOrElse(Seq())
}

我想在处理的每个阶段添加日志记录 - 所以我需要引入 writer monad 并将其与 monad 转换器 OptionT 堆叠。你能建议怎么做吗?

标签: scalamonad-transformersscala-catswriter-monad

解决方案


最好的方法是将您的服务调用转换为使用cats-mtl.

对于表示TryOption您可以使用MonadError,对于记录,您可以使用FunctorTell. 现在我不知道你在userServiceor中到底在做什么standService,但我写了一些代码来演示结果可能是什么样子:

type Log = List[String]

//inside UserService
def findOneByEmail[F[_]](email: String)
  (implicit F: MonadError[F, Error], W: FunctorTell[F, Log]): F[User] = ???

//inside StandService
def list[F[_]]()
  (implicit F: MonadError[F, Error], W: FunctorTell[F, Log]): F[List[Stand]] = ???

def getStandsByUser[F[_]](email: String)
(implicit F: MonadError[F, Error], W: FunctorTell[F, Log]): F[List[Stand]] =
  for {
    user <- userService.findOneByEmail(email)
    stands <- standService.list()
  } yield stands.filter(stand => user.stands.contains(stand.id))


//here we actually run the function
val result =
  getStandsByUser[WriterT[OptionT[Try, ?], Log, ?] // yields WriterT[OptionT[Try, ?], Log, List[Stand]]
    .run  // yields OptionT[Try, (Log, List[Stand])]
    .value // yields Try[Option[(Log, List[Stand])]]

这样我们就可以避免所有对不同服务的调用liftF并轻松组合我们的不同服务,即使它们在运行时会使用不同的 monad 转换器。


推荐阅读