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]] = ???