Lens in Scala

函数式访问器在haskell里被叫做Lens。在面向对象语言里这个没有什么必要,不过作为练习,我们看如何在scala表示van Laarhoven lens.

先给出haskell里的lens类型:

type Lens s a = forall f. Functor f => (a -> f a) -> (s -> f s)

这里有一个全局量词forall f,所以等价的scala lens类型我们得用一个generic函数来表示:

trait Lens[S, A] {
  def apply[F[_] : Functor](l: A => F[A])(s: S) : F[S]
}

上面在语义上,apply函数必须对所有的functor都满足,所以等价于forall f。

有了类型,我们开始加构造器。

haskell的lens构造器:

lens :: (s -> a) -> (s -> a -> s) -> Lens s a
lens getter setter l b = setter b <$> l (getter b)

其实可以证明任何一个lens构造器其实和这个都是等价的,因为((s->a), (s->a->s))和forall f. Functor f => (a -> f a) -> (s -> f s)是等价的。

对应的scala lens构造器:

object Lens {
  def apply[S, A](getter: S => A, setter: S => A => S) = new Lens[S, A] {
    def apply[F[_] : Functor](l: A => F[A])(s: S) : F[S] = {
      val m = implicitly[Functor[F]]
      m.fmap[A, S](l(getter(s)))(setter(s))
    } 
  }
}

有了lens,我们现在要加get和set函数了。

在加这两个函数前,我们需要两个functor: Const和Identity。

这里只给出scala版本的:(haskell版本的可以看这里

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

case class Const[A, B](a: A, b: B)

object ConstFunctor {
  implicit def constFunctor[C] = new Functor[({type c[a] = Const[C, a]})#c] {
    def fmap[A, B](a: Const[C, A])(f: A => B) = new Const(a.a, f(a.b))
  }
}

case class Identity[A](a: A)

object IdentityFunctor {
  implicit def identityFunctor = new Functor[Identity] {
    def fmap[A, B](a: Identity[A])(f: A => B) = new Identity(f(a.a))
  }
}

使用Const和Identity,我们可以得到get和set,这里也只给出scala版本的:

trait Lens[S, A] {
  import ConstFunctor._
  import IdentityFunctor._
  def apply[F[_] : Functor](l: A => F[A])(s: S) : F[S]
  def get(s: S) : A = apply[({type C[a] = Const[A, a]})#C]((a: A) => new Const(a, a))(s).a
  def set(s: S, a: A) = apply[Identity]((_: A) => new Identity(a))(s).a
}

好了,最后给个用例:

case class A(a: Int, b: Int)

object Main {
  
  def main(args: Array[String]) {
    val l = Lens1((a: A) => a.a, (a: A) => {(b: Int) => new A(b, a.b)})
    println(l.get(l.set(A(2, 6), 3)))
  }

}
posted @ 2015-03-16 12:40  wehu  阅读(788)  评论(0编辑  收藏  举报