为什么我不能flatMap试试?

时间:2014-08-30 14:22:08

标签: scala

鉴于

val strings = Set("Hi", "there", "friend")
def numberOfCharsDiv2(s: String) = scala.util.Try { 
  if (s.length % 2 == 0) s.length / 2 else throw new RuntimeException("grr") 
}

为什么我不能平面显示方法调用产生的Try?即。

strings.flatMap(numberOfCharsDiv2)
<console>:10: error: type mismatch;
 found   : scala.util.Try[Int]
 required: scala.collection.GenTraversableOnce[?]
              strings.flatMap(numberOfCharsDiv2)

for {
  s <- strings
  n <- numberOfCharsDiv2(s)
} yield n
<console>:12: error: type mismatch;
 found   : scala.util.Try[Int]
 required: scala.collection.GenTraversableOnce[?]
            n <- numberOfCharsDiv2(s)

但是,如果我使用Option而不是Try,则没有问题。

def numberOfCharsDiv2(s: String) = if (s.length % 2 == 0) 
  Some(s.length / 2) else None
strings.flatMap(numberOfCharsDiv2) # =>  Set(1, 3)

在Try上不允许使用flatMap的原因是什么?

4 个答案:

答案 0 :(得分:11)

让我们看一下flatMap的签名。

def flatMap[B](f: (A) => GenTraversableOnce[B]): Set[B]

您的numberOfCharsDiv2被视为String => Try[Int]Try不是GenTraversableOnce的子类,这就是您收到错误的原因。您并不需要仅提供Set的函数,因为您在flatMap上使用Set。该函数基本上必须返回任何类型的集合。

那为什么它适用于OptionOption也不是GenTraversableOnce的子类,但在Option随播广告对象中存在隐式转化,会将其转换为List

implicit def option2Iterable[A](xo: Option[A]): Iterable[A] = xo.toList

然后还有一个问题。为什么不对Try进行隐式转换?因为你可能得不到你想要的东西。

flatMap可以看作是map后跟flatten

想象一下,你有List[Option[Int]] List(Some(1), None, Some(2))。然后,flatten将为您提供List(1,2)类型的List[Int]

现在看一下Try的示例。类型为List(Success(1), Failure(exception), Success(2))的{​​{1}}。

现在如何解决失败问题?

  • 它应该像List[Try[Int]]一样消失吗?那为什么不直接使用None
  • 是否应该包含在结果中?那么它将是Option。这里的问题是类型为List(1, exception, 2),因为您必须找到List[Any]Int的公共超类。你失去了类型。

这些应该是没有隐式转换的原因。当然,如果你接受上述后果,你可以自己定义一个。

答案 1 :(得分:6)

问题在于,在您的示例中,您不会尝试平面图。你正在做的平面图超过了Set。

Flatmap over Set采用Set [A],函数从A到Set [B]。正如Kigyo在下面的评论中指出的那样,这不是Scala中Set的实际类型签名,但平面地图的一般形式是:

M[A] => (A => M[B]) => M[B]

也就是说,它需要一些更高级的类型,以及对更高级别类型的类型元素进行操作的函数,并且它会使用映射元素返回相同的高级类型。

在您的情况下,这意味着对于Set的每个元素,flatmap需要调用一个带有String的函数,并返回一个类型B的Set,它可以是String(或者可以是其他任何东西)。 / p>

你的功能

numberOfCharsDiv2(s: String)

正确地接受一个字符串,但错误地返回一个Try,而不是另一个Set作为flatmap需要。

如果您使用&#39; map&#39;您的代码将起作用,因为它允许您采用某种结构 - 在这种情况下,设置并运行每个元素的函数将其从A转换为B而不使用函数& #39; s返回类型符合封闭结构,即返回Set

strings.map(numberOfCharsDiv2)

res2: scala.collection.immutable.Set[scala.util.Try[Int]] = Set(Success(1), Failure(java.lang.RuntimeException: grr), Success(3))

答案 2 :(得分:1)

这是Scala 2.11中的Monad:

Browser

答案 3 :(得分:1)

Kigyo很好地解释了为什么Scala不隐式地这样做。简单地说,Scala不想自动丢弃Try中保留的异常。

Scala确实提供了一种简单的方法来将Try显式转换为Option。这是您可以在平面图中使用“尝试”的方式:

strings.flatMap(numberOfCharsDiv2(_).toOption)