我有两个问题,但我希望答案交织在一起。所以我正在玩flatMapping不同的参数类型。我得到以下内容:
val s: List[String] = List("f2", "df", "e") //> s : List[String] = List(f2, df, e)
val o = s.map(s => if (s.head == 'f')Some(s) else None)
//> o : List[Option[String]] = List(Some(f2), None, None)
val o1 = s.flatMap(s => if (s.head == 'f')Some(s) else None)
//> o1 : List[String] = List(f2)
val a: Option[String] = Some("Hello") //> a : Option[String] = Some(Hello)
val a1 = a.map(s => s.toList) //> a1 : Option[List[Char]] = Some(List(H, e, l, l, o))
但
val a2 = a.flatMap(s => s.toList) //gives
//type mismatch; found : List[Char] required: Option[?]
所以我试图理解o1编译背后的逻辑而不是a2。然后调查选项我想知道为什么Option不继承特性:Seq和Set? Option是Seq,因为它维护顺序,它是一个Set,因为它不包含重复项。通过Seq和Set,它将继承自Iterable和Traversable。
答案 0 :(得分:7)
所有GenTraversableOnce
后代的假设是它们包含任意个元素。有太多的API和假设或依赖于此的机制,例如Builder
和CanBuildFrom
。
然而,在更深层次上,重要的是要认识到for-comprehensions和map
/ flatMap
是 monadic 操作。 Monads不可互换 - 你不能选择一个函数A => N[B]
并将其传递给M[A]
以获得N[B]
,对于任何monad M和N,以及Option
和集合是不同的 monad。
通过许多隐含的魔法,所有集合都被视为单个monad,这导致人们假设所有monad 应可以互换,而事实并非如此。
然后考虑这样一个简单的案例:
val x = Option(1)
val y = List('a', 'b', 'c')
val z = for {
a <- x
b <- y
} yield (a, b)
z
的类型不能为Option
,因为结果有多个元素。它工作的唯一方法是使它成为类似Iterable
的东西。这可能对Option
有意义,如果你把它看作最多一个元素的集合,但对于像State
或Reader
monad这样的东西没有意义。 / p>
谈到将Option
视为最多一个的集合,这是不这样做的另一个原因。 Option
应该被认为是元素的存在与否,而不是集合,以及可用于帮助这种微妙区别的方法。再说一次,我知道有很多人认为这个论点至少是完全虚假的,所以要把它当作一粒盐。
答案 1 :(得分:1)
虽然可以映射选项,但它不是序列也不是集合(只有一个元素)。
选项上的平面地图定义如下
flatMap[B](f: (A) ⇒ Option[B]): Option[B]
所以它想要一个返回另一个Option的函数。你的代码
a.flatMap(s => s.toList)
不返回Option [_]但返回字符列表(s是String,s.toList返回List [Char])。