简化嵌套函子变换

时间:2018-09-29 13:48:05

标签: scala functional-programming monads functor

说我有2个函子f0f1,并且我有一些类似-的代码

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

2 个答案:

答案 0 :(得分:2)

假设f0: F0[X]f1: F1[Y]都具有F0实例的F1Functor,而{{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而言并不构成。所以没有 从MonadF[_]自动获取嵌套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?

[3] https://typelevel.org/cats/api/cats/data/OptionT.html

[4] https://typelevel.org/cats/api/cats/data/Nested.html