【问题标题】:Which Monad Transformer to use?使用哪个 Monad 变压器?
【发布时间】:2017-02-17 05:41:17
【问题描述】:

我正在尝试编写下面的验证函数,以便在遇到第一个错误后停止验证。 three 的返回类型与其他函数不同。我使用哪个 monad 转换器来编译这段代码?

import scalaz._
import Scalaz._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global


def one(a : String): Disjunction[Int, String] =
  a == "one" match {
    case true => \/-("one")
    case false => -\/(2)
  }

def two(a : String): Disjunction[Int, String] =
  a == "two" match {
    case true => \/-("two")
    case false => -\/(3)
  }

def three(a : String): Future[Disjunction[Int, String]] =
  Future (a == "three") map {
    case true => \/-("three")
    case false => -\/(4)
  }

def validate(a : String) = for {
  e1 <- one(a)
  e2 <- two(a)
  e3 <- EitherT(three(a))
} yield (e1 |+| e2 |+| e3)

编译错误:

Error:(27, 7) type mismatch;
 found   : scalaz.EitherT[scala.concurrent.Future,Int,String]
 required: scalaz.\/[?,?]
  e3 <- EitherT(three(a))
     ^
Error:(66, 7) type mismatch;
 found   : scalaz.EitherT[scala.concurrent.Future,Int,String]
 required: scalaz.\/[?,?]
  e3 <- EitherT(three(a))
     ^

【问题讨论】:

  • 有什么理由不将onetwo包装在FutureAwaitthree的结果中?
  • 你想看看EitherT monad 转换器,你的第三个&lt;- 没有返回String,它给了你整个Disjunction,这可能不是你想要的(前两个访问析取的String 部分)。
  • @EndeNeu 我尝试了 EitherT 但仍然出现编译错误。请参阅更新的问题以及编译错误。我之前使用过 DisjunctionT(它只是 EitherT 的包装器),结果相同。

标签: scala monads scalaz monad-transformers


【解决方案1】:

在这种情况下,您可以采用两种通用方法。第一个是让你的所有方法返回你知道你将使用的堆栈(在这种情况下EitherT[Future, Int, ?]),或者你可以让每个单独的方法返回最准确地捕获其自身效果的类型,然后引发当你编写它们时,你会得到适当的值。

如果您确切知道该用法的样子,第一种方法可以使用法在语法上更方便,但后一种方法更灵活,在我看来通常是更好的选择。在您的情况下,它看起来像这样:

import scalaz._, Scalaz._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

def one(a: String): Disjunction[Int, String] = (a == "one").either("one").or(2)
def two(a: String): Disjunction[Int, String] = (a == "two").either("two").or(3)

def three(a: String): EitherT[Future, Int, String] = EitherT(
  Future(a == "three").map(_.either("three").or(4))
)

def validate(a: String) = for {
  e1 <- EitherT.fromDisjunction[Future](one(a))
  e2 <- EitherT.fromDisjunction[Future](two(a))
  e3 <- three(a)
} yield (e1 |+| e2 |+| e3)

然后:

scala> validate("one").run.foreach(println)
-\/(3)

scala> validate("x").run.foreach(println)
-\/(2)

如果出于某种原因,您想在 for-comprehension 中使用一个普通的旧 Future,您可以使用 .liftM[EitherT[?[_], String, ?]] 将其提升到 EitherT[Future, String, A]

(请注意,此方法可能不是非常有用,因为它永远不会成功(字符串不能同时等于 "one""two""three"),但在至少组合奏效了。)

关于如何更一般地选择 monad 转换器堆栈:您只需将类型翻过来,以便 Future[Disjunction[Int, ?]] 变为 EitherT[Future, Int, ?] 等。具体而言,在这种情况下,Future 没有 monad 转换器(它是不可遍历,并且不可能在没有阻塞的情况下实现FutureT),所以无论如何你都知道它必须在内部进行。

【讨论】:

    【解决方案2】:

    Travis 的回答没有什么可补充的(和往常一样),但如果您在 Play 中使用它!申请,也许https://github.com/Kanaka-io/play-monadic-actions可以提供一些帮助。

    【讨论】:

    • 可以吗?|运算符可以在播放控制器之外使用吗?
    猜你喜欢
    • 2018-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-13
    • 1970-01-01
    • 1970-01-01
    • 2011-12-26
    • 2016-11-03
    相关资源
    最近更新 更多