scala haskell monad - arosh/arosh.github.com GitHub Wiki

Haskell

  • fmap :: (a -> b) -> f a -> f b
  • pure :: a -> f a
  • <*> :: f (a -> b) -> f a -> f b
  • join : f (f a) -> f a
  • return :: a -> m a
  • = (bind) :: m a -> (a -> m b) -> m b

>>= :: m a -> (a -> m b) -> m b
(pure f) :: m (a -> m b)
(pure f) <*> (m a) :: m (m b)
join $ (pure f) <*> (m a) :: m b
(m a) >>= f = join $ (pure f) <*> m a

Scala

  • map (fa: F[A])(f: A => B): F[B]
  • unit(コンストラクタ) (a: A): F[A]
  • <*> (fa: F[A])(ff: F[A => B]): F[B]
  • flatten (ffa: F[F[A]]): F[A]
  • (return相当のものはコンストラクタ)
  • flatMap (ma: M[A])(f: A => M[B]): M[B]
map(ma: M[A])(f: A => B): M[B]
flatMap(ma: M[A])(f: A => M[B]): M[B] = flatten( map( ma, a => map( unit(f), ff => ff(a) ) )
// = ma map { a => unit(f) map { ff => ff (a) } } flatten
// = unit(f) map { ma map _ } flatten

TypeClassopedia

Functor 写像できる何か

trait Functor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] }

関数もFunctor?

R => A という 関数fa に A => B という関数fを適用すると R => B という関数ができる 返り値 ret は ret(x) = f(fa(x))

→ もしかして:関数の合成

以上より、Functorは「写像できる『コンテナ』」だけでは無いということが分かる。(「計算のコンテクスト」などと言ったりする)

Functorの法則 ・何もしない関数 identity(x) = x で写像すると、 map(fa)(identity) == fa ・合成関数に関する法則 map(fa)(f compose g) == map(map(fa)(g))(f)

Applicative

def parse(s: String): Option[Int] = … があるとき、 parse(str).map { (x: Int) => { (y: Int) => x + y } } の型は残念ながら Option[Int => Int]

Applicativeを定義しよう

trait Applicative[F[_]] extends Functor { def <*>[A, B](fa: F[A])(f: F[A => B]) : F[B] }

使い方がキモすぎる

(parse("3")).<*>(parse("Nope").map { (x: Int) => { (y: Int) => x + y } }

ApplicativeBuilder というのを使わないといけないあたりがアレ (parse("3") |@| parse("Nope"))(_ + _)

演習:Option型に対するApplicativeファンクタのインスタンス(<*>とpoint)を定義せよ

implicit val OptionApplicative = new Applicative { def <*>[A, B](fa: Option[A])(f: Option[A => B]): Option[B] = for( fs <- f; a <- fa ) yield fs(a) def point[A](a: A): F[A] = some(a) }

モナドは像だ

モナドは高階関数をサポートする

f: () => Option[String] // 例:ファイルの読み込み g: String => Option[String] // 例:toInt

という関数があるとする。

f map g の返り値は残念ながら Option[Option[String]] モナドを返す関数の合成?は flatMap を使うべし

Scalaの flatMap はHaskellでいう bind, >>=

mapしてからflattenすればflatMapメソッドが作れるが、 その逆も可能。

これには、関数型言語的には unit 、Haskell的には return と呼ばれている概念が必要で、Scala的にはコンストラクタとかファクトリとか呼んでいるアレらしい。pointとも呼ぶかもしれない。

unit: A => M[A]

つまり、unit(x) == List(x) だったり unit(x) == Some(x) だったりするらしい。

Scalaで使う分には

flatten <-> unit(コンストラクタ)

と考えておけばいい?

forを使ったシンタックスシュガー

for (x <- expr) yield resultExpr は次と等価 expr map {x => resultExpr} モナド的にはこっち expr flatMap {x => unit(resultExpr)}

for { n <- ns o <- os } yield n * o

ns flatMap { n => os.flatMap { o => unit(n * o) } }

yield を使わない for は foreach になる

「右辺と左辺が等価である」とは何か 前提:どちらも、評価した時に副作用を生じない

  • 外から見て、同じふるまいをする
  • 値、ハッシュコード、isInstanceOf などで区別できない

Q. モナド則は何のためにあるか? A. 掛け算における 1. 1 * a == a 2. a * b == b * a 3. (a * b) * c == a * (b * c) のようなもの。演算を手助けする。

ファンクターとは? => 写像変換が行えるもの モナドはファンクターの一種

ファンクター第1法則:同一性(Identity) def indentify[A](x: A) = x と定義したとき identify(x) == x

これから言えること: xには副作用がない

ファンクターの第2法則:コンポジション(Compostion) ☆ f や g が副作用を発生しない時 ☆ m map g map f == m map { x => f(g(x)) }

これから言えること:関数の適用タイミングは関係ない

flattenは何をしているのか?

m flatMap f === flatten(m map f) fにidentifyを代入する

m flatMap identify === flatten(m map identify) m map identify === m より m flatMap identify === flatten(m) 両辺を入れ替えて flatten(m) === m flatMap identify

モナド則その1:同一性(Identity) m flatMap unit === m

モナド則その2:Unit unit(x) flatMap f === f(x)

モナド則その3:コンポジション(Composition)

Zeroの第1法則:同一性 mzero flatMap f = mzero

Zeroの第2法則:中身全てをZeroにしてflattenすると、親もZeroになる m flatMap { x => mzero } === mzero

Zeroの第3、第4法則 Zero + Zero == Zero Zero + m == m m + Zero == m

これらの法則を用いて、filterを定義する

m filter p == m flatMap { x => if(p(x)) unit(x) else mzero }

すなわち、Zeroが欲しければ、m filter false を呼べば良い