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)))

  }



}

你可能感兴趣的:(scala)