search term:

モナドはメタファーではない


Scala@djspiewak  2010 Monads Are Not Metaphors 


20101227 Daniel Spiewak  2011529 e.e d3si9n 

 (: ) 

姿


Java  interface  Ruby :

def foo(bar) puts bar bar.size end

 Ruby :

def foo(bar) puts bar; bar.size end

Ruby 便 returnfoo  size 

 (;)  Scala :
def foo(bar: String) = {
  ({ () => println(bar) })()
  ({ () => bar.length })()
}

Scala 

 Ruby size  String  Ruby 使:
def foo(bar: String) = {
  ({ () => println(bar) } andThen { () => bar.length })()
}

(:  andThen  0-arity  Unit  1-)

使使 (()) andThen :
def funcSyntax[A](f1: () => A) = new {
  def andThen[B](f2: () => B) = f1(); f2()
}

見方によっては、関数に直接適用するか文のレベルで間接的はたらくかの違いこそあれ、セミコロン「演算子」の能力を文字通り内包するメソッドを定義したと考えることができる。それはそれで面白い考えだが、重要なのは、まず最初の関数を実行し、その結果を捨てたあと、第二の関数を実行して、その結果を返しているということだ。

これが任意の数の関数に適用できることは明らかだろう。例えば:

def foo(bar: String) = {
  ({ () => println("Executing foo") } andThen
   { () => println(bar) } andThen
   { () => bar.length })()
}



: :
case class Thing[+A](value: A)

これは恐らく想像できる限り最も単純なコンテナだろう(実は、これはまさに想像できる限り最も単純なコンテナなのだが、その点は今は重要ではない)。値を Thing で囲う以外は何もできない:

val a = Thing(1)
val b = Thing(2)

ここで頭を設計モードに切り替えて欲しい。以下のようなコードを頻繁に書かなくてはいけないと想像して欲しい:

def foo(i: Int) = Thing(i + 1)
 
val a = Thing(1)
val b = foo(a.value)        // => Thing(2)

Thing  Thing使 Thing 使:
def foo(i: Int) = i + 1
 
val a = 1
val b = foo(a)              // => 2

 Thing 使

使 Thing Thing : a b

Thing  Thing Thing :
case class Thing[+A](value: A) {
  def bind[B](f: A => Thing[B]) = f(value)
}

よって、ある Thing があれば、その値を取り出し新たな Thing を計算する、というステップを一発で実行できる:

def foo(i: Int) = Thing(i + 1)
 
val a = Thing(1)
val b = a bind foo          // => Thing(2)

Thing 




 Thing :
val a = Thing(1)

 Thing  unit Haskell  return  A => Thing  Thing 
a bind { i => Thing(i + 1) }

 bind  Thing 使 Thing Scala  flatMap Haskell  >>= bind 使

: 使




Option


:
def firstName(id: Int): String = ...    // データベースから取得
def lastName(id: Int): String = ...
 
def fullName(id: Int): String = {
  val fname = firstName(id)
  if (fname != null) {
    val lname = lastName(id)
    if (lname != null)
      fname + " " + lname
    else
      null
  } else {
    null
  }
}

 (firstName  lastName) null 使 firstName  lastName  if

 Thing :
def firstName(id: Int): Thing[String] = ...    // データベースから取得
def lastName(id: Int): Thing[String] = ...
 
def fullName(id: Int): Thing[String] = {
  firstName(id) bind { fname =>
    if (fname != null) {
      lastName(id) bind { lname =>
        if (lname != null)
          Thing(fname + " " + lname)
        else
          Thing(null)
      }
    } else {
      Thing(null)
    }
  }
}



bind  null  bind Thing  Option :
sealed trait Option[+A] {
  def bind[B](f: A => Option[B]): Option[B]
}
 
case class Some[+A](value: A) extends Option[A] {
  def bind[B](f: A => Option[B]) = f(value)
}
 
case object None extends Option[Nothing] {
  def bind[B](f: Nothing => Option[B]) = None
}

Some  Thing Option :  Some  None None  Thing(null) 

Some  None  bind Some  bind  Thing Some  Thing None  None  bind fullName :
def firstName(id: Int): Option[String] = ...    // データベースから取得
def lastName(id: Int): Option[String] = ...
 
def fullName(id: Int): Option[String] = {
  firstName(id) bind { fname =>
    lastName(id) bind { lname =>
      Some(fname + " " + lname)
    }
  }
}

 if firstName  lastName  Thing(null)  None None  bind  None fullName firstName  lastName  None  Some 

 Groovy  (?.)Raganwald  Ruby  andand 

IO


 Haskell  IO:  PerlIO  Thing  Option 

Haskell  Ruby :

def foo(bar) puts bar bar.size end

size  in-place 

STDOUT = []

def foo(bar) STDOUT += [bar] bar.size end

Haskell Scala  var Java  final in-place in-place puts  puts 

Scala  println  Vector :
def foo(bar: String, stdout: Vector[String]) = {
  val stdout2 = println(bar, stdout)
  (bar.length, stdout2)
}
 
def println(str: String, stdout: Vector[String]) = stdout + str

 stdout  println  stdout 

 println  Haskell 

Vector[String] readLine  println readLine 

Haskell Phillip Wadler

println  Vector[String] :  Vector[String] Universe :
def foo(bar: String, everything: Universe) = {
  val everything2 = println(bar, everything)
  (bar.length, everything2)
}
 
def println(str: String, everything: Universe) = everything.println(str)

 Universe println  Universe 

Universe 

Phillip Wadler  IO:
def foo(bar: String): IO[Int] = {
  println(bar) bind { _ => IO(bar.length) }
}
 
def println(str: String): IO[Unit] = {
  // TODO 魔法の呪文をここに書く
}

 println  IO println 

IO Haskell IO 使:
def readLine(): IO[String] = {
  // TODO 魔法の呪文をここに書く
}
 
def fakeReadLine(str: String): String = {
  val back: IO[String] = readLine()
  back.get      // whew!  doesn't work
}

 IO使

Java Scala  Ruby Scala println varScala  IO Scala  IO IO Thing  Option State 使


使 Option 

x  x50

Option Option 使Option StateParserSTM Option  State 

 Component  Swing  sequence :
trait Monad[+M[_]] {
  def unit[A](a: A): M[A]
  def bind[A, B](m: M[A])(f: A => M[B]): M[B]
}
 
implicit object ThingMonad extends Monad[Thing] {
  def unit[A](a: A) = Thing(a)
  def bind[A, B](thing: Thing[A])(f: A => Thing[B]) = thing bind f
}
 
implicit object OptionMonad extends Monad[Option] {
  def unit[A](a: A) = Some(a)
  def bind[A, B](opt: Option[A])(f: A => Option[B]) = opt bind f
}
 
 
def sequence[M[_], A](ms: List[M[A]])(implicit tc: Monad[M]) = {
  ms.foldRight(tc.unit(List[A]())) { (m, acc) =>
    tc.bind(m) { a => tc.bind(acc) { tail => tc.unit(a :: tail) } }
  }
}

これを小ぎれいにする方法はいくらでもあるが、説明のために全てを明示的に書きだした。sequence の一般的な機能は、モナドのインスタンスの List を受け取り、それらの要素を含んだ List のモナドを返すことだ。以下に具体例で説明する:

val nums = List(Some(1), Some(2), Some(3))
val nums2 = sequence(nums)           // Some(List(1, 2, 3))

これはどのモナドにも適用できる:

val nums = List(Thing(1), Thing(2), Thing(3))
val nums2 = sequence(nums)           // Thing(List(1, 2, 3))

Monad  unit  bind  Scala  implicit 

便Haskell 




 unit bind

 Monad 使:
def axioms[M[_]](implicit tc: Monad[M]) {
  // 単位元律 1
  def identity1[A, B](a: A, f: A => M[B]) {
    val ma: M[A] = tc.unit(a)
    assert(tc.bind(ma)(f) == f(a))
  }
 
  forAll { (x, f) => identity1(a, f) }        // ScalaCheckっぽいもの
 
  // 単位元律 2
  def identity2[A](ma: M[A]) {
    assert(tc.bind(ma)(tc.unit) == ma)
  }
 
  forAll { m => identity2(m) }
 
  // 結合律
  def associativity[A, B, C](m: M[A], f: A => M[B], g: B => M[C]) {
    val mf: M[B] = tc.bind(m)(f)
    val mg: M[C] = tc.bind(mf)(g)
 
    val mg2: M[C] = tc.bind(m) { a =>
      tc.bind(f(a))(g)
    }
 
    assert(mg == mg2)
  }
 
  forAll { (m, f, g) => associativity(m, f, g) }
}

 unit bind bind  unit bind 

 bind  bind   bind 

便bind  bind 使 bind  bind :
val opt: Option[String] = Some("string")
 
opt bind { str => 
  val innerOpt = Some("Head: " + str)
  innerOpt bind { str => Some(str + " :Tail") }
}
 
// これは、以下と同様だ
 
opt bind { str => Some("Head: " + str) } bind { str => Some(str + " :Tail") }

 (sequential)

使


使

 Java  (sequential computation)  (composability)