Last active
April 21, 2016 15:00
-
-
Save oxbowlakes/0b54a97ee28caecf16af to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object Lenses { | |
/* | |
This gist shows that LensP (modification inside a functor) is isomorphic to LensR (getter and setter) | |
In particular, it shows that you can derive the signature: S => A from (A => F[A]) => (S => F[S]) | |
This is puzzling at first glance, because it gives rise to the question: | |
"if I end up with an F[S], how can I get a A out of it?" | |
We use the Const type where Const[A, X] = X. Note that Const[_, X] is a functor, but will always return | |
the value X which was originally stored in it! | |
*/ | |
/* traditional lens, with a getter and a setter */ | |
trait LensR[S, A] { | |
def viewR: S => A | |
def setR(a: A): S => S | |
} | |
import language.higherKinds | |
trait Functor[F[_]] { def fmap[A, B](fa: F[A])(f: A => B): F[B] } | |
object Functor { def apply[F[_]: Functor]: Functor[F] = implicitly } | |
/* Lens codification using a single method, modify an S by modifying its inner A within a functor */ | |
trait LensP[S, A] { def run[F[_]: Functor](modF: A => F[A]): S => F[S] } | |
/* Note we wish to prove that LensR is isomorphic to LensP */ | |
/* Separate the machinery we will use to prove the isomorphism */ | |
object Machinery { | |
import language.implicitConversions | |
import language.reflectiveCalls | |
/* Id machinery */ | |
object Identity { | |
type Id[A] = A | |
implicit def ToId[A](a: A): Id[A] = a | |
implicit def FromId[A](ida: Id[A]): A = ida | |
implicit val IdFunctor: Functor[Id] = new Functor[Id] { def fmap[A, B](fa: Id[A])(f: A => B): Id[B] = f(fa) } | |
} | |
/* Const machinery */ | |
object Constant { | |
type Const[A, X] = X | |
/* Note these are not implicit, for clarity, but they easily could be */ | |
def constPut[A, B](b: B): Const[A, B] = b | |
def constGet[A, B](c: Const[A, B]): B = c | |
def ConstFunctor[X] = new Functor[({ type l[a] = Const[a, X]})#l] { | |
override def fmap[A, B](fa: Const[A, X])(f: A => B): Const[B, X] = constPut[B, X](fa /* fa is an X */ ) | |
} | |
} | |
} | |
/* Now we are ready to prove the isomorphism */ | |
/* All LensPs are LensRs */ | |
def LensP_Is_LensR[S, A](lensP: LensP[S, A]): LensR[S, A] = new LensR[S, A] { | |
/* This is pretty obvious: we want to use the Identity functor */ | |
import Machinery.Identity._ | |
override def setR(a: A): S => S | |
= s => lensP.run[Id](_ => a).apply(s) | |
/* | |
Here is the magic: "how do we get an A out of an F[S]?" | |
If we choose F[S] = Const[A, S], then we have a functor which we can get an A out of | |
*/ | |
import Machinery.Constant._ | |
override def viewR: S => A | |
= s => constGet[S, A](lensP.run[({type l[a] = Const[a, A]})#l](a => constPut[S, A](a))(ConstFunctor[A])(s)) | |
/* with implicit Const, can be simplified to lensP.run[({type l[a] = Const[a, A]})#l](a => a).apply(s) */ | |
} | |
/* All LensRs are LensPs */ | |
def LensR_Is_LensP[S, A](lensR: LensR[S, A]): LensP[S, A] = new LensP[S, A] { | |
override def run[F[_] : Functor](modF: A => F[A]): S => F[S] = { | |
s => Functor[F].fmap(modF(lensR.viewR(s)))(a => lensR.setR(a)(s)) | |
} | |
} | |
/* LensPs can be trivially composed */ | |
def composeLensP[S, A, B](lensP1: LensP[S, A], lensP2: LensP[A, B]): LensP[S, B] = new LensP[S, B] { | |
override def run[F[_] : Functor](modF: B => F[B]): S => F[S] = { | |
lensP1 run (lensP2 run modF) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment