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

}

}


分享给朋友:
您可能感兴趣的文章:
随机阅读: