Daniel Steger for openphoto.net

IO モナド 

論文の後半を読むかわりに Rúnar さん (@runarorama)Towards an Effect System in Scala, Part 2: IO Monad を読もう:

While ST gives us guarantees that mutable memory is never shared, it says nothing about reading/writing files, throwing exceptions, opening network sockets, database connections, etc.

以下に ST の型クラスコントラクトをもう一度:

sealed trait ST[S, A] {
  private[effect] def apply(s: World[S]): (World[S], A)

そしてこれが IO の型クラスコントラクトだ:

sealed trait IO[+A] {
  private[effect] def apply(rw: World[RealWorld]): Trampoline[(World[RealWorld], A)]

Trampoline の部分を無視すると、IOST の状態を RealWorld に固定したものに似ている。ST 同様に IO object 下にある関数を使って IO モナドを作ることができる。

scala> import scalaz._, Scalaz._, effect._, IO._
import scalaz._
import Scalaz._
import effect._
import IO._

scala> val action1 = for {
         _ <- putStrLn("Hello, world!")
       } yield ()
action1: scalaz.effect.IO[Unit] = scalaz.effect.IOFunctions$$anon$4@149f6f65

scala> action1.unsafePerformIO
Hello, world!

以下が IO の下にある IO アクションだ:

  /** Reads a character from standard input. */
  def getChar: IO[Char] = ...
  /** Writes a character to standard output. */
  def putChar(c: Char): IO[Unit] = ...
  /** Writes a string to standard output. */
  def putStr(s: String): IO[Unit] = ...
  /** Writes a string to standard output, followed by a newline.*/
  def putStrLn(s: String): IO[Unit] = ...
  /** Reads a line of standard input. */
  def readLn: IO[String] = ...
  /** Write the given value to standard output. */
  def putOut[A](a: A): IO[Unit] = ...
  // Mutable variables in the IO monad
  def newIORef[A](a: => A): IO[IORef[A]] = ...
  /**Throw the given error in the IO monad. */
  def throwIO[A](e: Throwable): IO[A] = ...
  /** An IO action that does nothing. */
  val ioUnit: IO[Unit] = ...

IO object の apply メソッドを使って独自のアクションを作ることもできる:

scala> val action2 = IO {
         val source = scala.io.Source.fromFile("./README.md")
action2: scalaz.effect.IO[scala.collection.immutable.Stream[String]] = scalaz.effect.IOFunctions$$anon$4@bab4387

scala> action2.unsafePerformIO.toList
res57: List[String] = List(# Scalaz, "", Scalaz is a Scala library for functional programming., "", It provides purely functional data structures to complement those from the Scala standard library., ...


Composing these into programs is done monadically. So we can use for-comprehensions. Here’s a program that reads a line of input and prints it out again:

def program: IO[Unit] = for {
  line <- readLn
  _    <- putStrLn(line)
} yield ()

IO[Unit] is an instance of Monoid, so we can re-use the monoid addition function |+|.


scala> (program |+| program).unsafePerformIO