【问题标题】:Simplifying nested functor transformations简化嵌套函子转换
【发布时间】:2019-03-05 06:10:06
【问题描述】:

假设我有 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)
  }

【问题讨论】:

  • 就我而言,它实际上类似于 — IO[Option[X]]
  • 我不想真正关心我价值的容器是什么,我想对里面的东西应用转换
  • @LuisMiguelMejíaSuárez 你们都在说什么? Future 突然从哪里来的? Functor 的哪个实现有一个 map,它接受一个参数?他们都带two arguments,功能通常排在第二位。不清楚您在问什么,请提供minimal reproducible example
  • @AndreyTyukin 我正在使用 Cats,因为 map 被添加为句法糖。
  • @Tushar 然后不清楚添加了什么语法糖。 f0f1 到底是什么。看起来它们不是Functor[F] 的实例。

标签: scala functional-programming monads functor


【解决方案1】:

假设f0: F0[X]f1: F1[Y],其中F0F1 都有Functor 实例,并且f: (X, Y) =&gt; Z,则for-comprehension 等效于

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

我不确定在这种情况下 for 理解是否比嵌套的 maps 干净得多。

【讨论】:

    【解决方案2】:

    我认为你所追求的原则上是不可能的。

    你所追求的漂亮的 for 语法可以翻译成类似的东西

    f0.flatMap(v0 => (f1.map(v1 => f0f1(v0, v1))))
    

    这里的问题是flatMap 部分。为了有一个flatMap,你需要一个 Monad,不仅仅是Functor,或者至少是FlatMap 实例。但是Monads,而不是Functors,不要写。所以没有 自动从F[_]G[_] 获取嵌套Monad 的方法,即使两者都 其中Monads。 [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]。在这种特殊情况下它对您没有帮助,但它可能会帮助您减少一些嵌套的maps。

    --

    [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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-01
      • 2019-02-23
      • 2015-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多