为什么scalaz semigroup | + |对于任何一种类型,不要将左结果作为最终结果,而是作为操作

时间:2014-10-16 13:23:20

标签: scala scalaz

这是来自scalaz文档的代码,所以基本上,如果值是左值,它将把这个左值作为最终结果并停止评估其余的,这非常有用。

for {
         e1 <- "event 1 ok".right
         e2 <- "event 2 failed!".left[String]
         e3 <- "event 3 failed!".left[String]
       } yield (e1 |+| e2 |+| e3)  // event 2 failed 

但是,我有这个代码,我使用 reduceLeft 来附加值

object processor {
    def evaluate(x: Int): \/[String, Int] = {
      if (x <= 3) x.right[String] else ("some exception about " + x).left[Int]
    }
  }

val result = (1 to 6).map(processor.evaluate).reduceLeft(_ |+| _) 
//\/-(-\/(some exception about 4some exception about 5some exception about 6))

累积了左边的值,这不是我想要的。我想reduceLeft引起的不同行为是“左”值已被评估,而操作(flatMap和map)则不会。

如何更改此代码以将左侧结果作为最终结果

2 个答案:

答案 0 :(得分:2)

您可以使用遍历语法

val result = (1 to 6).toList.traverseU(processor.evaluate)

我将范围转换为List以获取范围

中的List类型

答案 1 :(得分:1)

认为可能会对以下代码中的内容产生一些疑惑

for {
  e1 <- "event 1 ok".right
  e2 <- "event 2 failed!".left[String]
  e3 <- "event 3 failed!".left[String]
} yield (e1 |+| e2 |+| e3)  // event 2 failed

在上面的代码中,for comprehension使用map和flatMap作为\/[+A, +B]。由于\/[+A, +B]的map / flatMap的实现,将永远不会评估yield语句定义的函数。在这种情况下,map / flatMap正在执行不同\/[String, _]合并。在|+|中函数使用的yield运算符在SemiGroup语法中定义,只是使用Semigroup[String]将右侧的字符串组合成一个字符串。在上面的情况中,它也可以使用String.append。我明白为什么人们可能希望在这里使用Semigroup[String]来组合这些与简单String.append相对立的原因,但重要的是,yield定义的函数使用的是Semigroup[String]而不是Semigroup[A \/ B]。 1}}。

在下面的案例中, 使用Semigroup[A \/ B]String \/ Int个实例合并为一个String \/ IntreduceLeft(或foldLeft如果你选择了那条路线)只是简单地将列表中每个元素连接到它传递的累积函数。

object processor {
    def evaluate(x: Int): \/[String, Int] = {
      if (x <= 3) x.right[String] else ("some exception about " + x).left[Int]
    }
  }

val result: String \/ Int = (1 to 6).map(processor.evaluate).reduceLeft(_ |+| _)

根据Semigroup[A \/ B]的定义,我们可以看到它需要Semigroup[A]Semigroup[B]

如果您使用Monad[A \/ _]Applicative[A \/ _]组合传递给A \/ B的函数中的reduceLeft,则不会合并A。< / p>

以下使用Applicative[A \/ _]

val result: String \/ Int = (1 to 6).map(processor.evaluate).reduceLeft {
  (xs, x) => (xs |@| x)(_ |+| _)
}

以下使用为A \/ B定义的map / flatMap,与顶部的代码最相似。

val result: String \/ Int = (1 to 6).map(processor.evaluate).reduceLeft {
  (xs, x) => for {
    xsA <- xs
    xA <- x
  } yield xsA |+| xA
}

foldMapM可能会做你想要的,但是使用Foldable [List] .foldsRight,所以你的错误会与你使用foldLeft时的错误不同。不幸的是,需要一个丑陋的lambda,或者下面的类型别名。

type StringEither[B]=String \/ B
val result: String \/ Int = (1 to 6).toList.foldMapM[StringEither, Int](processor.evaluate)