Scala for-comprehension with 元组分解

Scala for-comprehension with tuple decomposition

for {
  a <- Some(1)
  b <- Some(2)
} yield (a, b)

returns Some((1, 2))

for {
  a <- Right(1).right
  b <- Left(2).left
} yield (a, b)

returns Left((1, 2))


现在我想分解 for comprehension 中的元组。

for {
  (a, b) <- Some((1, 2))
  (c, d) <- Some((3, 4))
} yield (a, b, c, d)

returns Some((1, 2, 3, 4))

for {
  (a, b) <- Right((1, 2)).right
  (c, d) <- Left((3, 4)).left
} yield (a, b, c, d)

编译失败:

error: constructor cannot be instantiated to expected type;
found   : (T1, T2)
required: scala.util.Either[Nothing,(Int, Int)]
                   (a, b) <- Right((1, 2)).right

error: constructor cannot be instantiated to expected type;
found   : (T1, T2)
required: scala.util.Either[(Int, Int),Nothing]

为什么最后一个示例不起作用?有什么区别?

这可能是 for 表达式的限制。翻译

for {
  (a, b) <- Some((1, 2))
  (c, d) <- Some((3, 4))
} yield (a, b, c, d)

进入

Some((1, 2)).flatMap({case(a, b) =>
  Some((3, 4)).map({case (c, d) =>
    (a, b, c, d)
  })
})

双向工作。使用 Either 表达式,只有 map/flatMap 版本有效。

for {
  (a, b) <- Right((1, 2)).right
  (c, d) <- Left((3, 4)).left
} yield (a, b, c, d)


Right((1, 2)).right.flatMap({
  case(a, b) => Left((3, 4)).left.map({case (c, d) =>
    (a, b, c, d)
  })
})

我不建议使用 Either,而是使用来自 \/ 的类型 鳞鳞鱼。 http://eed3si9n.com/learning-scalaz/Either.html Either 不是 左倾或右倾,这是一个问题,因为它没有指定 错误或值的去向。

因为 (Any, Any) <- Either 的生成器不是 "irrefutable" 过滤器被添加到去糖代码中 (why does filter have to be defined for pattern matching in a for loop in scala?),导致:

Right((1, 2)).right.filter { case (a, b) => true; case _ => false }.flatMap({
  case(a, b) => Left((3, 4)).left.filter { case (c, d) => true; case _ => false }.map({case (c, d) =>
    (a, b, c, d)
  })
})

过滤器是编译错误的地方,因为右边的过滤器方法是这样的(左边的类似):

def filter[X](p: B => Boolean): Option[Either[X, B]] = e match {
  case Left(_) => None
  case Right(b) => if(p(b)) Some(Right(b)) else None
}

这意味着编译器正在尝试执行以下操作:

(T1, T2) match {
  case Left(_) => None
  case Right(b) => if(p(b)) Some(Right(b)) else None
}

失败,因为 (T1, T2) 无法转换为 Either[A, B](右延伸),其中 A 为 Nothing,B 为 (Int, Int)。

您可以通过以下方式获得接近于此的结果:

for {
  a <- Right((1, 2)).right
  b <- Left((3, 4)).left
} yield (a, b) match {
  case ((c, d), (e, f)) => (c, d, e, f)
  case _ => 
}

这是一个错误:

SI-5589: For-comprehension on Either.RightProjection with Tuple2 extractor in generator fails to compile

withFilter() 被调用(一些文档引用 filter(),但在 2.8 中已更改),这会混淆类型推断。

withFilter() 用于 for(a <- b if c) 之类的东西,但根据 6.19 它不应该在这种情况下使用。

后一个bug在已经开放七年(2008年)的SI-1336: spec requires type checking of for-comprehension to consider refutability中捕获。

也许后代会找到解决方法。


why does filter have to be defined for pattern matching in a for loop in scala?