up:: Programming

概要

関数型言語の考え。Monad。
関数に与えられた「ある値をもとに、別の値を返す」デザインパターン。
与えられた値は計算に絶対使う必要はない。捨ててもいい。

考えとしては単純で、その規則に基づくと関数内で呼出し関数を分けているセミコロン(;)でさえモナド的な考えということになる。
(第一関数の結果は第一関数のまま、それと関係なく第二関数の値を返している)

例えばこんな風に有ったらThingはモナド。

 
case class Thing[+A](value: A) {
  def bind[B](f: A => Thing[B]) = f(value)
}
 
def foo(i: Int) = Thing(i + 1)
 
val a = Thing(1)
val b = a bind foo  

ThingというコンテナにAという値が入る。bindを呼ぶとBの関数(にAを使用して)を呼んで戻り値を返す。
つまり、Aを使ってBの結果を返している。よってモナド。

もし何かから始めて、それを分解した後で、同じ型の別の何かを計算した時、それはモナドだ。

こんなの普通の関数もそうだろというところだが、その通り。
モナドは大きな概念で、どこにでもある。

Thingは値をラッピングしているもの。モナドではこれをUnitと呼ぶ。
bindはThingから値を取り出し、渡された関数を用いて新たなThingを作る。ある物から始めて、その値を使って新たなものを計算している。なのでモナド。

何に使うの

なんの役に立つの? というところだが、これを拡張するといろいろ省略して書ける。
例えば同じ状態に対し同じ条件文をたくさん使うなら、その状態を取る値をUnitに入れておき、bindで条件文を書いてみる。するとbindを呼べば条件文を適用できるし、言語によっては複数の条件文を一気にbindで計算することもできる。

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
}
 
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)
    }
  }
}

Haskell IO

Haskell(純粋関数型言語。変数が無く副作用を許さない)のIO。
これもモナド。

副作用無しで画面などを弄りたい。すると手っ取り早いのはUnitに前の状態、先ほどのbindに前の状態と変えたい対象の関数を入れて新たな状態を返すこと。
しかしこれは標準入力だとうまくいかない。何を読んで出力するのかは人の手が入るため、いつか必ず別の何かを変化させることになる。

ならば状態を世界単位にすれば何とかなる。実際は世界の振りをしたもの。それがIO。

ただし、IOに入った値は何も取り出せない。ここをどうしてるかはHaskellの話。

役立ち部分

モナドはUnitとbindを持つ。
そして値の種類は気にしない。どんな値でもUnitに入ってれば同じ扱いが出来る。
そしてUnitはtrait(interfaceみたいなの)をつけて抽象化できる。どんな値を入れるかを継承できる。

つまり、どの特定のモナドなのか知らずともモナドを扱うことは出来る。

モナド則

モナドをモナドたらしめるものとしてモナド則という物がある。数学の交換法則とかそういうの。
知らなくてもさっきの通り説明は出来た。

最初二つの公理は、Unit関数はbind関数に対して単純なコンストラクタであるというもの。
bindがばらしてもunitが格納していた値のままだし、bindが値をモナドで囲むだけなら何もしてないのと同じ。

三つ目の公理はbindの順序が変わっても最後の値は同じだよ的なこと。

これらのおかげで手続き的になり読みやすい。

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