Scala for-comprehension with tuple decomposition

时间:2015-09-18 11:50:03

标签: scala for-comprehension

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

返回Some((1, 2))

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

返回Left((1, 2))

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

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

返回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]

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

3 个答案:

答案 0 :(得分:6)

这是一个错误:

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,在这种情况下不应使用它。

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

也许下一代会找到修复。

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

答案 1 :(得分:4)

因为(Any,Any)&lt; - 的生成器都不是“无可辩驳的”过滤器被添加到desugared代码(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)
  })
})

过滤器是编译错误发生的地方,因为Right的过滤器方法看起来像这样(左边的类似):

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)无法转换为[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 _ => 
}

答案 2 :(得分:3)

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

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,而是使用\/类型 scalaz。 http://eed3si9n.com/learning-scalaz/Either.html Either不是 左,右倾,这是一个问题,因为它没有指明 错误或值的位置。