Reputation:
So in scala we have the typical Lens signature as:
case class Lens[O,V](get: O => V, set: (O,V) => O)
But as you can see, it only updates and sets values of the same type, it does not set one type for another. What I have in mind is something more like this:
case class Lens[O[_],A,B](get: O[A] => A, set: (O[A],B) => O[B])
With A
and B
make sense for O[_]
My question is. Does this stop being isomorphic? Is there a simpler way without breaking some rules?
Upvotes: 3
Views: 121
Reputation: 497
In haskell lens and in monocle, polymorphic lenses have 4 type parameters. They are equivalent to the following implementation:
case class PLens[S, T, A, B](get: S => A, set: B => S => T)
Then monomoprhic lenses are simply a type alias:
type Lens[S, A] = PLens[S, S, A, A]
You can read the 4 type parameters as: if I change an A to B inside of S then I get a T.
e.g.
S = (Int, String)
T = (Long, String)
A = Int
B = Long
Upvotes: 1
Reputation: 39366
I think that to figure out the right Lens
abstraction, it would be helpful to have a concrete lens-able type in mind.
However, for your particular example, there is something we can say:
case class Lens[O[_],V[_],A,B](get: O[A] => V[A], set: (O[A],V[B]) => O[B])
I do not think that this kind of lens can be composed. In order to compose lenses, the result of a get
has to be able to feed into set
. But here, the result of a get
is a V[_]
, whereas set
needs an O[_]
.
As a further explanation, here is another possible kind of polymorphic lens, but it is probably not one that fits your needs:
trait Lens[T[_]] {
def get[A](t: T[A]): A
def set[A,B](t: T[A], x: B): T[B]
}
It can be composed like so:
def composeLenses[T[_],U[_]](lens1: Lens[T], lens2: Lens[U]) =
new Lens[({type x[A] = T[U[A]]})#x] {
def get[A](t: T[U[A]]): A = lens2.get(lens1.get(t))
def set[A,B](t: T[U[A]], x: B): T[U[B]] = lens1.set(t, lens2.set(lens1.get(t), x))
}
I wouldn't have been able to figure out the definition of Lens
abstractly -- in order to do it I had to use this concrete case:
case class Box[A](x: A)
def boxBoxGet[A](b: Box[Box[A]]): A = ???
def boxBoxSet[A,B](b: Box[Box[A]], x: B): Box[Box[B]] = ???
Upvotes: 1