如何将A[B[C]]转换为B[A[C]],如果A和B是单子?

12

我正在寻求一种更通用的解决方案,利用单子(可能是幺半群)来实现与if( xs.contains(None) ) None else Some(xs.flatten)相同的结果,对于类型为Seq[Option[A]]xs

如何使用Scalaz实现这个功能?我觉得我漏掉了一些明显的东西。

1个回答

14

对于M来说,有两个单子是不够的,而对于N来说则是足够的,这当然是不够的。但如果M具有Traverse实例并且N具有Applicative实例,则可以使用sequence。例如:

import scalaz._, Scalaz._

def foo[A](xs: List[Option[A]]): Option[List[A]] = xs.sequence
这具有您所需的语义。请注意,我正在使用 List 而非 Seq,因为 Scalaz 7 不再为 Seq 提供必要的 Traverse 实例(尽管您可以很容易地编写自己的实现)。
正如您已经注意到的,以下内容不会编译:
List(Some(1), Some(45)).sequence

虽然在其中抛出一个None也可以:

scala> List(Some(1), None, Some(45)).sequence
res0: Option[List[Int]] = None
这是因为推断出的 List(Some(1), Some(45)) 的类型将会是 List[Some[Int]],而我们没有 SomeApplicative 实例。
Scalaz 提供了一个方便的 some 方法,它的作用与 Some.apply 相似,但是可以直接获得一个已经被定义为 Option 类型的值,因此你可以这样写:
scala> List(some(1), some(45)).sequence
res1: Option[List[Int]] = Some(List(1, 45))

无需额外输入。


在使用Scalaz 7的REPL时,它会失败,因为它找不到Seq的Traverse typeclass实例。也许我应该导入其他东西? - Nikita Volkov
你可以使用 List 替代 Seq,或者为 Seq 提供自己的实例 - 我不确定为什么 Seq 实例在 7 中消失了。 - Travis Brown
谢谢,我已经解决了!但是我对它的行为感到沮丧:除了Seq问题之外,还有另一个需要您明确指定类型的问题:val xs = List(Some(1), Some(45)); (xs : List[Option[Int]]).sequence - Nikita Volkov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接