Scala 为什么可以';我不试试平面图吗?

Scala 为什么可以';我不试试平面图吗?,scala,Scala,给定 为什么我不能将方法调用导致的尝试平面化?i、 e 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") } 不允许在尝试时使用flatMap的理由是什么?问题在于,在您的示例中,您并没有在尝试时使用

给定

为什么我不能将方法调用导致的尝试平面化?i、 e

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") 
}

不允许在尝试时使用flatMap的理由是什么?

问题在于,在您的示例中,您并没有在尝试时使用flatMap。你正在做的平面图设置过度

集合上的平面映射采用集合[a],函数从a到集合[B]。正如Kigyo在下面的评论中指出的,这并不是Scala中设置的flatmap的实际类型签名,但flatmap的一般形式是:

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

也就是说,它采用某种更高种类的类型,以及一个对该更高种类类型中的类型元素进行操作的函数,它将返回具有映射元素的相同更高种类类型

在您的例子中,这意味着对于集合的每个元素,flatmap都需要调用一个接受字符串的函数,并返回一个类型为B的集合,该集合可以是字符串(也可以是其他任何类型)

你的职能

def numberOfCharsDiv2(s: String) = if (s.length % 2 == 0) 
  Some(s.length / 2) else None
strings.flatMap(numberOfCharsDiv2) # =>  Set(1, 3)
正确地获取字符串,但错误地返回Try,而不是flatmap所需的另一个集

如果您使用“map”,代码将起作用,因为它允许您采用某种结构——在本例中为Set,并在每个元素上运行函数,将其从a转换为B,而函数的返回类型不符合封闭结构,即返回一个Set

strings.map(numberOfCharsDiv2)


res2:scala.collection.immutable.Set[scala.util.Try[Int]=Set(成功(1)、失败(java.lang.RuntimeException:grr)、成功(3))
让我们看看
flatMap
的签名

numberOfCharsDiv2(s: String)
您的
numberOfCharsDiv2
被视为
String=>Try[Int]
Try
不是
GenTraversableOnce
的子类,因此会出现错误。仅因为在
集合
上使用
flatMap
,就不需要提供
集合的函数。该函数基本上必须返回任何类型的集合

那么为什么它与
选项
一起工作呢
选项
也不是
GenTraversableOnce
的子类,但在
选项
伴生对象中存在一个隐式转换,将其转换为
列表

def flatMap[B](f: (A) => GenTraversableOnce[B]): Set[B]
还有一个问题。为什么不同时对
Try
进行隐式转换呢?因为你可能得不到你想要的

flatMap
可以看作是一个
map
,后跟一个
flatten

假设您有一个
列表[选项[Int]]
列表(一些(1),没有,一些(2))
。然后,
plant
将为您提供类型为
List[Int]
列表(1,2)

现在看一个带有
Try
的示例<代码>列表(成功(1)、失败(异常)、成功(2))
类型为
List[Try[Int]]

现在,扁平化如何应对失败

  • 它是否应该像
    None
    那样消失?那么为什么不直接使用
    选项
  • 是否应将其包括在结果中?然后是
    列表(1,异常,2)
    。这里的问题是类型是
    List[Any]
    ,因为您必须为
    Int
    Throwable
    找到一个通用的超类。你输了

这就是为什么没有隐式转换的原因。当然,如果您接受上述后果,您可以自己定义一个单子。

它是Scala 2.11中的单子:

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

Kigyo很好地解释了为什么Scala没有隐式地做到这一点。简单地说,Scala不希望自动丢弃在一次尝试中保留的异常

Scala确实提供了一种简单的方法来显式地将Try转换为选项。以下是如何在平面图中使用Try:

scala> import scala.util._ 
import scala.util._

scala> val x: Try[String] = Success[String]("abc")
x: scala.util.Try[String] = Success(abc)

scala> val y: Try[String] = Failure[String](new Exception("oops"))
y: scala.util.Try[String] = Failure(java.lang.Exception: oops)

scala> val z = Try(x)
z: scala.util.Try[scala.util.Try[String]] = Success(Success(abc))

scala> val t = Try(y)
t: scala.util.Try[scala.util.Try[String]] = Success(Failure(java.lang.Exception: oops))

scala> z.flatten
res2: scala.util.Try[String] = Success(abc)

scala> t.flatten
res3: scala.util.Try[String] = 
Failure(java.lang.UnsupportedOperationException: oops)

使用
选项
仅当存在隐式转换
选项[A]=>Iterable[A]
时才有效。我想问一下这种转换背后的原理是什么,这样我们就可以把
选项
想象成一个最大为1的列表。超级方便,不是吗?我一直使用
选项[A]=>Iterable[A]
隐式转换。我认为将选项视为0对1容器非常有用。
flatMap
over
Set[a]
需要一个来自
a=>gentraversableone[B]
的函数。它不需要是一个
集合[B]
。是的,我意识到这是一个具体的例子,我试图更多地讨论flatmap的一般情况,或者haskell中的bind,其结构是:(M[A])(A=>M[B]):M[B]。不幸的是,这回答了一个被误解的问题。回答得不错,特别是关于类型的部分,
None
不包含任何值,可以轻松安全地删除它们,
故障
相反,它包含一个有意义的值,因此不能(也不应该)删除,但保留它们意味着,如果存在转换,您最终将失去
Any
的类型信息和安全列表,我想这是一个非常有力的论点。我理解你的答案,但我认为这不是代表std库的正确设计。数据类型是
Try[A]
,因此我只关心
A
s。我应该能够消除
Try
,丢弃
Failure
值-就像
选项
语义一样。是的,
Try
只有一个通用参数。但这仅仅是因为
故障
的类型已设置为
可丢弃
,不需要进一步的规范。当您也关心异常时,可以使用
Try
。如果您不这样做:从一开始就使用
选项
,或者在
上使用
选项
方法尝试
。对于您的情况:
strings.flatMap(numberOfCharsDiv2(u).toOption)
+1,但可以说
Try
在概念上是
的一种专门化。其中,
仅处理正确的值
implicit def option2Iterable[A](xo: Option[A]): Iterable[A] = xo.toList
scala> import scala.util._ 
import scala.util._

scala> val x: Try[String] = Success[String]("abc")
x: scala.util.Try[String] = Success(abc)

scala> val y: Try[String] = Failure[String](new Exception("oops"))
y: scala.util.Try[String] = Failure(java.lang.Exception: oops)

scala> val z = Try(x)
z: scala.util.Try[scala.util.Try[String]] = Success(Success(abc))

scala> val t = Try(y)
t: scala.util.Try[scala.util.Try[String]] = Success(Failure(java.lang.Exception: oops))

scala> z.flatten
res2: scala.util.Try[String] = Success(abc)

scala> t.flatten
res3: scala.util.Try[String] = 
Failure(java.lang.UnsupportedOperationException: oops)
strings.flatMap(numberOfCharsDiv2(_).toOption)