查看Scala中的Functional Programming中的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 ()
Run Code Online (Sandbox Code Playgroud)
我决定converter用一个重写flatMap.
def converterFlatMap: IO[Unit] = PrintLine("enter a temperate in degrees F").
flatMap(x => ReadLine.map(_.toDouble)).
flatMap(y => PrintLine((y + 32).toString))
Run Code Online (Sandbox Code Playgroud)
当我用最后一个替换flatMap时map,我没有看到在控制台上打印出readLine的结果.
用flatMap:
enter a temperate in degrees
37.0
Run Code Online (Sandbox Code Playgroud)
用map:
enter a temperate in degrees
为什么?另外,签名(IO[Unit])如何与map或仍然相同flatMap?
这是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 }
}
Run Code Online (Sandbox Code Playgroud)
我认为Scala在第二种情况下将IO [IO [Unit]]转换为IO [Unit].尝试在scala控制台中运行这两个变体,并且不要为其指定类型def converterFlatMap: IO[Unit],您将看到差异.
至于为什么map不起作用,从IO的定义可以清楚地看出:当你映射到IO [IO [T]]时,map里面只会调用run外部IO,结果将是IO [IO [T] ],因此,只有前两个PrintLine和ReadLine将被执行.
flatMap也将执行内IO,并导致将是IO[T]其中T是类型参数A内的IO,因此,所有三个语句将被执行.
PS:我认为你错误地扩展了理解力.根据规则,你写的for循环应扩展为:
PrintLine("enter a temperate in degrees F").flatMap { case _ =>
ReadLine.map(_.toDouble).flatMap { case d =>
PrintLine((d + 32).toString).map { case _ => ()}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,在此版本中嵌套了flatMaps/maps.
PPS:实际上最后一个for语句也应该是flatMap,而不是map.如果我们假设scala有一个"返回"运算符,它将值放入monadic上下文中,
(例如,return(3)将创建IO [Int],它什么也不做,它的函数run返回3.),
那么我们可以重写 for (x <- a; y <- b) yield y为
a.flatMap(x => b.flatMap( y => return(y))),
但是因为与scala中的最后一个语句b.flatMap( y => return(y))完全相同b.map(y => y)的工作理解被扩展为map.