型クラスでHaskellのguardを定義してみる(未完)
@mzpさん主催の非公式名古屋Scalaに行ってきました。
前半は@yoshihiro503さんによるモナド講座。後半はCoq、F#、AmazonEC2、MessagePackなど「り、りろんはしってる」な話が飛び交ってました。
モナド講座のなかでfor文のifはfilterが必要だったり、先頭に置けなかったりでイケてない、って話でした。
一方Haskellのdoではguardっていうのが使えてこれが良い、Scalaでもがんばればそれっぽいものはできる、ということだったのでちょっと頑張ってみました。
結論から書くとあまりうまくはいきませんでした。どなたかアドバイスください…。
Scalaでguardを作る
ゴールとしてはこうなってほしい。
scala> for(a <- List(1,2,3,4); _ <- guard(a%2 == 1)) yield a res0: List[Int] = List(1, 3)
Haskellでのguardはこう定義されている。
guard :: (MonadPlus m) => Bool -> m () guard True = return () guard False = mzero
これをScalaに持込みたい。当初Monadの扱いがよくわからなかったが、SOでのMonadの話や@kmizuさんのdefault(T)を作る話や型クラス襲来を見ているうちに、型クラスをいうものを使えばうまくいきそうだと思い至った。
まずはMonadPlusを定義する。
trait MonadPlus[M[_]]{ def unit[A](a:A): M[A] //def bind ... def mzero[A]: M[A] //def mplus ... }
bindとmplusは今回使わないので省略。
そしてguardを定義。implicit parameter以外はHaskellでの定義と同じ。
def guard[A[_]](exp:Boolean)(implicit m:MonadPlus[A]) = if(exp)m.unit(()) else m.mzero
implicit parameterに入るのはこれ。
implicit object MonadicOption extends MonadPlus[Option]{ def unit[A](a: A) = Some(a) def mzero[A] = None }
guardを使ってみる
実際にfor文でguardを使ってみる
scala> for(_ <- guard(true); b <- Some("test")) yield b res1: Option[java.lang.String] = Some(test) scala> for(_ <- guard(false); b <- Some("test")) yield b res2: Option[java.lang.String] = None
いい感じ。しかし、
scala> for(_ <- guard(true); b <- List(1,2,3)) yield b <console>:9: error: type mismatch; found : List[Int] required: Option[?] for(_ <- guard(true); b <- List(1,2,3)) yield b
ならばと以下を定義
implicit object MonadicList extends MonadPlus[List]{ def unit[A](a: A) = List(a) def mzero[A] = Nil }
あらためて実行。
scala> for(_ <- guard(true); b <- List(1,2,3)) yield b <console>:10: error: ambiguous implicit values: both object MonadicList in object $iw of type object MonadicList and object MonadicOption in object $iw of type object MonadicOption match expected type MonadPlus[A] for(_ <- guard(true); b <- List(1,2,3)) yield b ^
む、無念…。ちなみに型を指定すればいける。
scala> for(_ <- guard[List](true); b <- List(1,2,3)) yield b res5: List[Int] = List(1, 2, 3) scala> for(_ <- guard[List](false); b <- List(1,2,3)) yield b res6: List[Int] = List() scala> for(a <- List(1,2,3,4); _ <- guard[List](a%2 == 1)) yield a res7: List[Int] = List(1, 3)
いろいろ試してみたけどついに型指定を消すことはできなかった…