来世から頑張る!!

技術ブログを目指して

MonadTって自動でMonadにはならないの???

Monadって難しいって話。

元ネタは xuweiさんのブログ

いやね、最近PlayでWebアプリ作ろうと頑張ってるんですよ。
そこでログ出力のために、引数で渡すものを必要に応じてWriterにできればなとか考えてたんですよ。
結果うまくはいかなかったんですが。

本編以外を極力省くため、試すコードはただの足し算。

def add(a: Int, b: Int): Int = a + b

これをブログを参考にIdでくるむ。

import scalaz._, Scalaz._

def add(a: Id[Int], b: Id[Int]): Id[Int] = for {
  x <- a
  y <- b
} yield x + y

// 実行
> val a = add(1, 2)
a: scalaz.Scalaz.Id[Int] = 3

それぞれIdに入っちゃったから取り出すためのfor書いて、中の値を使って処理。

これをさらにブログに書いてある通り、任意の入れ物から取り出せるように変更。

import scalaz._, Scalaz._
import scala.language.higherKinds

def add[F[_]](a: F[Int], b: F[Int])(implicit F: Monad[F]): F[Int] = for {
  x <- a
  y <- b
} yield x + y

// 実行
> val a = add[Id](1, 2)
a: scalaz.Scalaz.Id[Int] = 3

[Id]って書かないと実行できなくなった。。。
まあそこはIdで使うかどうかなのできっと問題無いのかな?
OptionやListで渡していれば予想通りの動き。

> val a = add(1.some, 2.some)
a: Option[Int] = Some(3)

> val b = add(List(1, 3, 5), List(7, 8))
b: List[Int] = List(8, 9, 10, 11, 12, 13)

MonadTransformer使って合成

> val a = List(1, 2, 3).liftM[OptionT]
a: scalaz.OptionT[List,Int] = OptionT(List(Some(1), Some(2), Some(3)))

> val b = List(5, 7).liftM[OptionT]
b: scalaz.OptionT[List,Int] = OptionT(List(Some(5), Some(7)))

> val c = add(a, b)
error: no type parameters for method add: (a: F[Int], b: F[Int])(implicit F: scalaz.Monad[F])F[Int] exist
so that it can be applied to arguments (scalaz.OptionT[List,Int], scalaz.OptionT[List,Int])

!?!?
OptionT[List, Int]なんてMonadじゃねぇぞ!!!って言われる。なんで?なんで?

よーしパパMonadとか実装しちゃうぞー(結婚したい)

type OptionTList[A] = OptionT[List, A]
implicit val OptionTListMonad = new Monad[OptionTList] {
  def point[A](a: => A) = List(a).liftM[OptionT]
  def bind[A, B](fa: OptionTList[A])(f: A => OptionTList[B]) = fa flatMap f
}

> val c = add(a, b)
error: no type parameters for method add: (a: F[Int], b: F[Int])(implicit F: scalaz.Monad[F])F[Int] exist
so that it can be applied to arguments (scalaz.OptionT[List,Int], scalaz.OptionT[List,Int])

通らない・・・

> val c = add[OptionTList](a, b)
c: OptionTList[Int] = OptionT(List(Some(6), Some(8), Some(7), Some(9), Some(8), Some(10)))

これなら通る。 つまりこう?

> val a: OptionTList[Int] = List(1, 2, 3).liftM[OptionT]
a: OptionTList[Int] = OptionT(List(Some(1), Some(2), Some(3)))

> val b: OptionTList[Int] = List(5, 7).liftM[OptionT]
b: OptionTList[Int] = OptionT(List(Some(5), Some(7)))

> val c = add(a, b)
c: OptionTList[Int] = OptionT(List(Some(6), Some(8), Some(7), Some(9), Some(8), Some(10)))

むぅー。。。
やり方を間違っている気しかしない。。。

やっぱり元ブログみたいにTraitでくるんでコンストラクターを中で呼び出さないとダメなのかな?
それとも、Monadの代わりに何かそれっぽいのがMonad Transformer用にある???

2015/04/23 追記 Scala勉強会にてねこはる先生に教えてもらった。

> val a = List(2, 3, 5).liftM[OptionT]
a: scalaz.OptionT[List,Int] = OptionT(List(Some(2), Some(3), Some(5)))

> val b = List(1, 9).liftM[OptionT]
b: scalaz.OptionT[List,Int] = OptionT(List(Some(1), Some(9)))

> val c = add[({type F[A] = OptionT[List, A]})#F](a, b)
c: scalaz.OptionT[[+A]List[A],Int] = OptionT(List(Some(3), Some(11), Some(4), Some(12), Some(6), Some(14))

Scala型推論オブジェクト指向的継承のために残念な部分が多い」ってのはこういうことなのかなぁ?