読者です 読者をやめる 読者になる 読者になる

Fight the Future



One Div Zero: Monads are Elephants Part 4の翻訳続き。


Ladies and Gentleman I Present the Mighty IO Monad(みなさん、すばらしいIOモナドを紹介します)

The IOAction.apply factory method takes an expression of type A and returns an IOAction[A]. It sure looks like "unit." It's not, but it's close enough for now. And if we knew what flatMap was for this monad then the monad laws would tell us how to create map using it and unit. But what's flatMap going to be? The signature needs to look like def flatMap[B](f: A=>IOAction[B]):IOAction[B]. But what does it do?

しかし何がflatMapであるべきでしょう?シグネチャはdef flatMap[B](f: A=>IOAction[B]):IOAction[B]のようなものを必要とします。しかし、それは何をするのでしょう?

What we want it to do is chain an action to a function that returns an action and when activated causes the two actions to occur in order. In other words, getString.flatMap{y => putString(y)} should result in a new IOAction monad that, when activated, first activates the getString action then does the action that putString returns. Let's give it a whirl.

それにやらせたいことはアクションを返す関数のための連鎖したアクションであり、活性化したとき2つのアクションを順に実行するものです。言い換えると、getString.flatMap{y => putString(y)}は新しいIOActionモナドとなるべきであり、活性化されると最初にgetStringアクションを活性化しそれからputStringが返すアクションを実行します。試してみましょう。

//file RTIO.scala
sealed abstract class IOAction_v4[+A] extends 
    Function1[WorldState, (WorldState, A)] {
  def map[B](f:A => B):IOAction_v4[B] = 
    flatMap {x => IOAction_v4(f(x))}  
  def flatMap[B](f:A => IOAction_v4[B]):IOAction_v4[B]= 
    new ChainedAction(this, f)
  private class ChainedAction[+A, B](
      action1: IOAction_v4[B], 
      f: B => IOAction_v4[A]) extends IOAction_v4[A] {
    def apply(state1:WorldState) = {
      val (state2, intermediateResult) = 
      val action2 = f(intermediateResult)

object IOAction_v4 {
  def apply[A](expression: => A):IOAction_v4[A] = 
    new SimpleAction(expression)

  private class SimpleAction[+A](expression: => A) 
      extends IOAction_v4[A] {
    def apply(state:WorldState) = 
      (state.nextState, expression)

// the rest remains the same
sealed trait WorldState{def nextState:WorldState}

abstract class IOApplication_v4 {
  private class WorldStateImpl(id:BigInt) ...

The IOAction factory and SimpleAction remain the same. The IOAction class gets the monad methods. Per the monad laws, map is just defined in terms of flatMap and what we're using as unit for now. flatMap defers all the hard work to a new IOAction implementation called ChainedAction.


The trick in ChainedAction is its apply method. First it calls action1 with the first world state. This results in a second world state and an intermediate result. The function it was chained to needs that result and in return the function generates another action: action2. action2 is called with the second world state and the tuple that come out is the end result. Remember that none of this will happen until the main driver passes in an initial WorldState object.


A Test Drive(テスト駆動)

At some point you may have wondered why getString and putString weren't renamed to something like createGetStringAction/createPutStringAction since that's in fact what they do. For an answer, look at what happens when we stick 'em in our old friend "for".


object HelloWorld_v4 extends IOApplication_v4 {
  import RTConsole_v4._
  def iomain(args:Array[String]) = {
        _ <- putString(
            "This is an example of the IO monad.");
        _ <- putString("What's your name?");
        name <- getString;
        _ <- putString("Hello " + name)
    } yield ()

It's as if "for" and getString/putString work together to create a mini language just for creating a complex IOActions.


Take a Deep Breath(深呼吸しましょう)

Now's a good moment to sum up what we've got. IOApplication is pure plumbing. Users subclass it and create a method called iomain which is called by main. What comes back is an IOAction - which could in fact be a single action or several actions chained together. This IOAction is just "waiting" for a WorldState object before it can do its work. The ChainedAction class is responsible for ensuring that the WorldState is changed and threaded through each chained action in turn.


getString and putString don't actually get or put Strings as their names might indicate. Instead, they create IOActions. But, since IOAction is a monad we can stick it into a "for" statement and the result looks as if getString/putString really do what they say the do.


It's a good start; we've almost got a perfectly good monad in IOAction. We've got two problems. The first is that, because unit changes the world state we're breaking the monad laws slightly (e.g. m flatMap unit === m). That's kinda trivial in this case because it's invisible. But we might as well fix it.

いいスタートです。IOActionにおけるほぼ完璧なできのモナドを手に入れました。問題が2つあります。1つめはunitが世界の状態を変更してしまうのでモナド則を少し破っているということです(たとえばm flatMap unit === m)。この場合見えないことなので取るに足らないことです。しかし、これも対応できるでしょう。

The second problem is that, in general, IO can fail and we haven't captured that just yet.


IO Errors(IOエラー)

In monadic terms, failure is represented by a zero. So all we need to do is map the native concept of failure (exceptions) to our monad. At this point I'm going to take a different tack from what I've been doing so far: I'll write one final version of the library with comments inline as I go.


The IOAction object remains a convenient module to hold several factories and private implementations (which could be anonymous classes, but it's easier to explain with names). SimpleAction remains the same and IOAction's apply method is a factory for them.


//file RTIO.scala
object IOAction {
  private class SimpleAction[+A](expression: => A) 
      extends IOAction[A] {
    def apply(state:WorldState) = 
      (state.nextState, expression)

  def apply[A](expression: => A):IOAction[A] = 
    new SimpleAction(expression)

UnitAction is a class for unit actions - actions that return the specified value but don't change the world state. unit is a factory method for it. It's kind of odd to make a distinction from SimpleAction, but we might as well get in good monad habits now for monads where it does matter.


  private class UnitAction[+A](value: A) 
      extends IOAction[A] {
    def apply(state:WorldState) = 
      (state, value)
  def unit[A](value:A):IOAction[A] = 
    new UnitAction(value)

FailureAction is a class for our zeros. It's an IOAction that always throws an exception. UserException is one such possible exception. The fail and ioError methods are factory methods for creating zeroes. Fail takes a string and results in an action that will raise a UserException whereas ioError takes an arbitrary exception and results in an action that will throw that exception.


  private class FailureAction(e:Exception) 
      extends IOAction[Nothing] {
    def apply(state:WorldState) = throw e
  private class UserException(msg:String) 
    extends Exception(msg)

  def fail(msg:String) = 
    ioError(new UserException(msg))    
  def ioError[A](e:Exception):IOAction[A] = 
    new FailureAction(e)

IOAction's flatMap, and ChainedAction remain the same. Map changes to actually call the unit method so that it complies with the monad laws.


I've also added two bits of convenience: >> and <<. Where flatMap sequences this action with a function that returns an action, >> and << sequence this action with another action.


It's just a question of which result you get back. >>, which can be pronounced "then", creates an action that returns the second result, so 'putString "What's your name" >> getString' creates an action that will display a prompt then return the user's response.

どちらの結果を戻すのかという疑問があります。>>は、「then」と発音しますが、2つ目の結果を返すアクションを生成するので、「putString "What's your name" >> getString」はプロンプトを表示しユーザーのレスポンスを戻すアクションを生成します。

Conversely, <<, which can be called "before" creates an action that will return the result from the first action.


sealed abstract class IOAction[+A] 
    extends Function1[WorldState, (WorldState, A)] {
  def map[B](f:A => B):IOAction[B] = 
    flatMap {x => IOAction.unit(f(x))}  
  def flatMap[B](f:A => IOAction[B]):IOAction[B]=
    new ChainedAction(this, f)

  private class ChainedAction[+A, B](
      action1: IOAction[B], 
      f: B => IOAction[A]) extends IOAction[A] {
    def apply(state1:WorldState) = {
      val (state2, intermediateResult) = 
      val action2 = f(intermediateResult)

  def >>[B](next: => IOAction[B]):IOAction[B] =
    for {
      _ <- this;
      second <- next
    } yield second
  def <<[B](next: => IOAction[B]):IOAction[A] =
    for {
      first <- this;
      _ <- next
    } yield first

Because we've got a zero now, it's possible to add a filter method by just following the monad laws. But here I've created two forms of filter method. One takes a user specified message to indicate why the filter didn't match whereas the other complies with Scala's required interface and uses a generic error message.


  def filter(
      p: A => Boolean, 
      msg:String):IOAction[A] =
    flatMap{x => 
      if (p(x)) IOAction.unit(x) 
      else IOAction.fail(msg)}
  def filter(p: A => Boolean):IOAction[A] =
    filter(p, "Filter mismatch")

A zero also means we can create a monadic plus. As some infrastructure for creating it, HandlingAction is an action that wraps another action and if that action throws an exception then it sends that exception to a handler function. onError is a factory method for creating HandlingActions. Finally, "or" is the monadic plus. It basically says that if this action fails with an exception then try the alternative action.


  private class HandlingAction[+A](
      handler: Exception => IOAction[A]) 
      extends IOAction[A] {
    def apply(state:WorldState) = {
      try {
      } catch {
        case e:Exception => handler(e)(state)

  def onError[B >: A](
      handler: Exception => IOAction[B]):
      IOAction[B] = 
    new HandlingAction(this, handler)      

  def or[B >: A](
      alternative:IOAction[B]):IOAction[B] =
    this onError {ex => alternative}

The final version of IOApplication stays the same


sealed trait WorldState{def nextState:WorldState}

abstract class IOApplication {
  private class WorldStateImpl(id:BigInt) 
      extends WorldState {
    def nextState = new WorldStateImpl(id + 1)
  final def main(args:Array[String]):Unit = {
    val ioaction = iomain(args)
    ioaction(new WorldStateImpl(0));
  def iomain(args:Array[String]):IOAction[_]

RTConsole stays mostly the same, but I've added a putLine method as an analog to println. I've also changed getString to be a val. Why not? It's always the same action.


//file RTConsole.scala
object RTConsole {
  val getString = IOAction(Console.readLine)
  def putString(s: String) = 
  def putLine(s: String) = 

And now a HelloWorld application to exercise some of this new functionality. sayHello creates an action from a string. If the string is a recognized name then the result is an appropriate (or inappropriate) greeting. Otherwise it's a failure action.


Ask is a convenience method that creates an action that will display a specified string then get one. The >> operator ensures that the action's result will be the result of getString.


processsString takes an arbitrary string and, if it's 'quit' then it creates an action that will say goodbye and be done. On any other string sayHello is called. The result is combined with another action using 'or' in case sayHello fails. Either way the action is sequenced with the loop action.


Loop is interesting. It's defined as a val just because it can be - a def would work just as well. So it's not quite a loop in the sense of being a recursive function, but it is a recursive value since it's defined in terms of processString which in turn is defined based on loop.


The iomain function kicks everything off by creating an action that will display an intro then do what the loop action specifies.


Warning: because of the way the library is implemented this loop will eventually blow the stack. Do not use it in production code. Read the comments to see why.


object HelloWorld extends IOApplication {
  import IOAction._
  import RTConsole._
  def sayHello(n:String) = n match {
    case "Bob" => putLine("Hello, Bob")
    case "Chuck" => putLine("Hey, Chuck")
    case "Sarah" => putLine("Helloooo, Sarah")
    case _ => fail("match exception")
  def ask(q:String) =
    putString(q) >> getString

  def processString(s:String) = s match {
    case "quit" => putLine("Catch ya later")
    case _ => (sayHello(s) or         
        putLine(s + ", I don't know you.")) >>
  val loop:IOAction[Unit] = 
    for {
      name <- ask("What's your name? ");
      _ <- processString(name)
    } yield ()
  def iomain(args:Array[String]) = {
        "This is an example of the IO monad.") >>
    putLine("Enter a name or 'quit'") >>


As for loop not being tail recursive - well, it can't be. The reason is a bit subtle. Loop isn't quite a normal loop. Instead, ultimately, it's an instance of ChainedAction. That's where the real problem is: as the library is designed ChainedAction's apply method cannot be tail recursive since its tail call must be to some arbitrary IOAction's apply method rather than to its own apply method.

One Div Zero: Monads are Elephants Part 4


Conclusion for Part 4(パート4の結論)

In this article I've called the IO monad 'IOAction' to make it clear that instances are actions that are waiting to be performed. Many will find the IO monad of little practical value in Scala. That's okay, I'm not here to preach about referential transparency. However, the IO monad is one of the simplest monads that's clearly not a collection in any sense.


Still, instances of the IO monad can be seen as containers. But instead of containing values they contain expressions. flatMap and map in essence turn the embedded expressions into more complex expressions.


Perhaps a more useful mental model is to see instances of the IO monad as computations or functions. flatMap can be seen as applying a function to the computation to create a more complex computation.


In the last part of this series I'll cover a way to unify the container and computation models. But first I want to reinforce how useful monads can be by showing an application that uses an elephantine herd of monads to do something a bit more complicated.