说我有2个函子f0
和f1
,并且我有一些类似-的代码
f0.map(v0 => f1.map(v1 => f0f1(v0, v1)))
有没有一种方法可以简化此过程,以便可以使用for表达式并使代码更简洁-
for {
v0 <- f0
v1 <- f1
} yield f0f1(v0, v1)
map
功能可通过如下形式的语法糖使用:
implicit class FunctorOps[F[_], A](fa: F[A]) {
def F = implicitly[Functor[F]]
def map[B](ab: A => B): F[B] = F.map(fa)(ab)
}
答案 0 :(得分:2)
假设f0: F0[X]
和f1: F1[Y]
都具有F0
实例的F1
和Functor
,而{{1} }-理解力等同于
f: (X, Y) => Z
将会
for
示例:
f0.map(x => f1.map(y => f(x, y)))
产生:
for (x <- f0) yield for (y <- f1) yield f(x, y)
而
val f0 = Option(42)
val f1 = List(1, 2, 3)
for (x <- f0) yield for (y <- f1) yield x * y
产生
res1: Option[List[Int]] = Some(List(42, 84, 126))
在这种情况下,我不确定for (y <- f1) yield for (x <- f0) yield x * y
的理解是否比嵌套的res2: List[Option[Int]] = List(Some(42), Some(84), Some(126))
更干净。
答案 1 :(得分:0)
我认为原则上不可能追求你。
您追求的不错的for
语法会转换为类似的
f0.flatMap(v0 => (f1.map(v1 => f0f1(v0, v1))))
这里的问题是flatMap
部分。要有一个flatMap
,您需要一个
Monad
,不仅是Functor
,或者至少是FlatMap
实例。但是Monad
相对于Functor
而言并不构成。所以没有
从Monad
和F[_]
自动获取嵌套G[_]
的方法,即使两者
其中有Monad
个。 [1]
使用某些monad转换器可能会获得不错的语法,但是它们 所有单子都不存在(也不能存在)。 [2]
对于有变压器的单子,可能会出现您想要的东西:
val l: List[Int] = List(1,2,3)
val o: Option[String] = Some("abc")
val ol: OptionT[List, String] = for {
a <- OptionT.liftF(l)
b <- OptionT.fromOption[List](o)
} yield a + b.toString
如果您对OptionT
感兴趣,则有scala文档([3])。
情人眼里是好还是坏?
可悲的是,如果您更需要此功能,则可能需要按照以下方式编写自己的帮助器功能
def combine2[F[_]: Functor, G[_]: Functor, A, B, C](
fa: F[A],
gb: G[B])(
f: (A, B) => C
): F[G[C]] =
fa.map(a => gb.map(b => (f(a, b))))
如果您不想这样做,您可以做的一件事就是已经提到的@ andrey-tyukin。但我同意,最好只嵌套对map
的调用。
您可能要看的另一件事是Nested
[4]。在这种情况下,它无济于事,但可以帮助您减少一些嵌套的map
。
-
[1] http://blog.tmorris.net/posts/monads-do-not-compose/
[2] Why is there no IO transformer in Haskell?