user1553111
user1553111

Reputation:

How to implement parametric lenses that change type of state

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

Answers (2)

Julien Truffaut
Julien Truffaut

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

Owen
Owen

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

Related Questions