【问题标题】:flatMap and For-Comprehension with IO Monad使用 IO Monad 的 flatMap 和 For-Comprehension
【发布时间】:2014-02-15 16:54:03
【问题描述】:

查看来自Functional Programming in Scala 的 IO Monad 示例:

def ReadLine: IO[String] = IO { readLine }
def PrintLine(msg: String): IO[Unit] = IO { println(msg) }

def converter: IO[Unit] = for {
  _ <- PrintLine("enter a temperature in degrees fahrenheit")
  d <- ReadLine.map(_.toDouble)
  _ <- PrintLine((d + 32).toString)
} yield ()

我决定用flatMap 重写converter

def converterFlatMap: IO[Unit] = PrintLine("enter a temperate in degrees F").
     flatMap(x => ReadLine.map(_.toDouble)).
       flatMap(y => PrintLine((y + 32).toString))

当我将最后一个flatMap 替换为map 时,我没有看到控制台上打印出readLine 的结果。

flatMap:

enter a temperate in degrees 
37.0

map:

enter a temperate in degrees

为什么?另外,签名(IO[Unit])与mapflatMap 仍然相同吗?

这是本书中的IO monad。

  sealed trait IO[A] { self =>
    def run: A
    def map[B](f: A => B): IO[B] =
      new IO[B] { def run = f(self.run) }
    def flatMap[B](f: A => IO[B]): IO[B] =
      new IO[B] { def run = f(self.run).run }
  }

【问题讨论】:

    标签: scala monads


    【解决方案1】:

    我认为 Scala 在第二种情况下将 IO[IO[Unit]] 转换为 IO[Unit]。尝试在 scala 控制台中运行这两种变体,并且不要为 def converterFlatMap: IO[Unit] 指定类型,您会看到不同之处。

    至于map为什么不起作用,从IO的定义就很清楚了: 当你在 IO[IO[T]] 上进行映射时,内部的映射只会在外部 IO 上调用 run,结果将是 IO[IO[T]],所以只会执行前两个 PrintLineReadLine .

    flatMap 也会执行内部 IO,结果会是IO[T],其中T 是内部IO 的类型参数A,所以这三个语句都会被执行。

    P.S.:我认为你错误地扩展了理解。根据rules,您编写的for循环应扩展为:

    PrintLine("enter a temperate in degrees F").flatMap { case _ =>
        ReadLine.map(_.toDouble).flatMap { case d =>
            PrintLine((d + 32).toString).map { case _ => ()}
        }
    }
    

    请注意,在这个版本中 flatMaps/maps 是嵌套的。

    P.P.S:实际上最后一个for 语句也应该是flatMap,而不是map。如果我们假设 scala 有一个“return”运算符将值放入一元上下文中,
    (例如 return(3) 将创建 IO[Int] 什么都不做,它的函数 run 返回 3。),
    然后我们可以将 for (x &lt;- a; y &lt;- b) yield y 重写为
    a.flatMap(x =&gt; b.flatMap( y =&gt; return(y))),
    但是因为 b.flatMap( y =&gt; return(y)) 的工作方式与 b.map(y =&gt; y) 的工作方式完全相同,因此 scala 中的最后一条语句用于理解被扩展为 map。

    【讨论】:

    • 但我们需要在for comprehension 的最后一行使用flatMap,因为正如您所指出的,map 只执行f(self.run),而我们需要f(self.run).run(平面图),对吧?我知道两者都可以编译,因为IOMonad#map#flatMap 都返回IO[Unit]
    • [也 - 你的例子有效(谢谢),但它应该是 PrintLine((d + 32))...]
    • 不,最后一行可能是一张地图,就像我的 p.s. 中一样,但理解的其余部分应该正确扩展。
    • 我很困惑PrintLine((d + 32).toString).map { case _ =&gt; ()} 如何调用最内层的PrintLine。看来,只读取mapflatMap 的签名,只有后者会在内部和外部IO[Unit] 上调用run
    • PrintLine((d + 32).toString).map { case _ =&gt; ()} 中没有内部 IO :)。它是一个单一的IO[Unit],它本身就是一个“内部”IO。第二个 flatMap,即 `ReadLine.map(_.toDouble).flatMap { case d =>` 在其上调用 run
    猜你喜欢
    • 1970-01-01
    • 2014-07-04
    • 1970-01-01
    • 2013-12-25
    • 2015-11-07
    • 2019-03-24
    • 2012-06-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多