Monad in Scala

Scala有很强的类型系统。加上一些隐式规则,我们可以在scala里模拟haskell的monad。

先从haskell的monad type class开始:

class Monad M where
  ret  :: a -> M a
  bind :: M a -> (b -> M b) -> M b

这里M是type class的参数。它是一个高阶类型, kind是 * –> *。认识到这个很重要,因为如果我们想在scala里面模拟,我们首先需要知道是否scala提供了相应的东西。

幸运的是,scala支持高级类型: M[_], 所以对应的我们可以用scala的traits表示如下:

trait Monad[M[_]] {
  def ret[A](a: A) : M[A]
  def bind[A, B](fa: M[A], f: A => M[B]) : M[B]
}

有了class,我们再看如何定义instance。

这里我们给出State实例,因为可以演示scala高级类型柯瑞化。

先给出state type:

data State s a = State { runState:: s -> (s, a)}

代数类型State有两个参数,所以它的kind是* –> * –> *。你可能发现它和Monad参数的kind不一样,那我们如何能给出State的Monad instance呢?

简单,先传一个参数(State s),这样它的kind就是* –> *了(柯瑞化),于是我们有了下面的instance。

instance Monad (State s) where
  ret a = State (\s –> (s, a))
  bind (State fa) f = State $ \s -> 
    let (s0, a0)   = fa s
        (State fb) = f a0
     in fb s0

对应的在scala里我们先定义代数类型State。可以用一般的class,但是最简单的是用case class。

case class State[S, A](runState : S => (S, A))

为了模拟对不同的type,对应不同的Monad实现,我们需要在scala里的type system里给出一个映射关系。

我们可以需要利用隐式参数:

object Monad {
  def apply[M[_]](implicit m : Monad[M]) : Monad[M] = m
}

这里有一个技巧,由于apply没有参数,Monad[T] === Monad.apply[T],于是我们可以通过Monad[T]来得到不同Monad实例。T的不同得到的实例也不同。

现在实现一个State的隐身规则,就可以相当于插入了一个从State到实例的映射关系。

object StateMonad {
  implicit def stateToMonad[S] = new Monad[({type M[a] = State[S, a]})#M] {
    def ret[A](a: A) = new State[S, A](s => (s, a))
    def bind[A, B](a: State[S, A], f: A => State[S, B]) = new State((s: S) => {
      val State(f0) = a
      val (s0, a0) = f0(s)
      val State(f1) = f(a0)
      f1(s0)
    })
  }
}

你可能发现了一个奇怪的东西 ({type M[a] = State[S, a]})#M。其实就是一个多参数类型的柯瑞化。

你可能像为什么Scala不支持这样写 State[S, _]?我也不知道。。。

scala只支持一个参数的量化,但是可以通过其他方法来完成柯瑞化。

上面的奇怪东西其实等价于下面的代码:

trait SM[S] {
  type M[a] = State[S, a]
}

({type M[a] = State[S, a]})#M == SM[S]#M

‘#’被用来访问内部类型别名。{…}其实就是一个匿名的类型,不过通常我们用来它来做structural typing。

好了,现在我们可以使用了:

import StateMonad._

def main {
  val s = Monad[SM[Int]#M].ret(0)
  Monad[SM[Int]#M].bind(s, (a:Int) => s)
}
posted @ 2015-03-11 00:05  wehu  阅读(1308)  评论(0编辑  收藏  举报