避免使用OptionT进行样板操作(自然变换?)

时间:2018-07-18 22:10:55

标签: scala functional-programming monads scalaz scala-cats

我有以下方法:

trait Tr[F[_]]{

    def getSet(): F[Set[String]]

    def checksum(): F[Long]

    def value(): F[String]

    def doRun(v: String, c: Long, s: Set[String]): F[Unit]
}

现在我想写以下内容以供理解:

import cats._
import cats.data.OptionT
import cats.implicits._

def fcmprhn[F[_]: Monad](F: Tr[F]): OptionT[F, Unit] =
  for {
    set <- OptionT {
      F.getSet() map { s =>
        if(s.nonEmpty) Some(s) else None
      }
    }
    checksum <- OptionT.liftF(F.checksum())
    v <- OptionT.liftF(F.value())
    _ <- OptionT.liftF(F.doRun(v, checksum, set))
    //can be lots of OptionT.liftF here
} yield ()

如您所见,OptionT样板太多了。有办法避免吗?

我认为我可以利用F ~> OptionT[F, ?]。你能建议点什么吗?

2 个答案:

答案 0 :(得分:1)

一种方法可能是将理解力的“仅F”部分嵌套在单个background-image: url('http://unsplash.it/1200x800'), linear-gradient(red, transparent); background-size: contain; background-repeat: no-repeat; background-position: right; 中:

liftF

答案 1 :(得分:1)

您可以改为以“ mtl样式”编写它。 mtl-style指的是haskell中的mtl库,但实际上它只是意味着我们将效果编码为具有抽象效果OptionT[F, ?]的函数,而不是将效果编码为值(即F[_]),并赋予{{ 1}}使用类型类的功能。这意味着不必使用F作为返回类型,而只需使用OptionT[F, Unit]作为我们的返回类型,因为F[Unit]必须能够处理错误。

这使编写类似您的代码的过程稍微容易一些,但是当您向堆栈中添加monad转换器时,效果会得到放大。现在,您只需要举起一次,但是如果将来需要F,该怎么办。使用mtl-style,您需要做的就是添加另一个类型类约束。

这是用mtl样式编写的代码:

StateT[OptionT[F, ?], S, Unit]

现在,当您运行程序时,可以将def fcmprhn[F[_]](F: Tr[F])(implicit E: MonadError[F, Unit]): F[Unit] = for { set <- OptionT { F.getSet() flatMap { s => if(s.nonEmpty) E.pure(s) else E.raiseError(()) } } checksum <- F.checksum() v <- F.value() _ <- F.doRun(v, checksum, set) } yield () 指定为类似于F[_]之前的样子:

OptionT[F, ?]