鉴于
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的原因是什么?
答案 0 :(得分:11)
让我们看一下flatMap
的签名。
def flatMap[B](f: (A) => GenTraversableOnce[B]): Set[B]
您的numberOfCharsDiv2
被视为String => Try[Int]
。 Try
不是GenTraversableOnce
的子类,这就是您收到错误的原因。您并不需要仅提供Set
的函数,因为您在flatMap
上使用Set
。该函数基本上必须返回任何类型的集合。
那为什么它适用于Option
? Option
也不是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)