Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scalaz Lens Composition

Really simple question here. After watching an excellent introduction to lenses:

http://www.youtube.com/watch?v=efv0SQNde5Q

I thought I might attempt one of the simple examples covered in the talk:

import scalaz.Lens._
fst.andThen(snd).set(((1,2),3),9)

this was followed by this error

error: type mismatch;
 found   : scalaz.Lens[(Nothing, Nothing),Nothing]
 required: scalaz.Lens[(Nothing, Nothing),C]
Note: Nothing <: C, but class Lens is invariant in type B.
You may wish to define B as +B instead. (SLS 4.5)
              fst.andThen(snd).set(((1,2),3))
                      ^

Any ideas as to how to make this work?

like image 792
billymillions Avatar asked Aug 03 '12 02:08

billymillions


2 Answers

You're going to need to help the compiler out a bit. Either of the following would do:

(fst andThen snd[Int, Int]).set(((1, 2), 3), 9)

or:

(fst[(Int, Int), Int] andThen snd).set(((1, 2), 3), 9)

My guess would be that Edward Kmett glossed over this issue in the talk because it's not really relevant to his subject—it's just one of the (annoying) quirks of Scala's type inference system. In Haskell, for example, the following would be fine:

setL (sndLens . fstLens) 9 ((1, 2), 3)

You can read the answers here for more information about the limitations of type inference in Scala.

like image 102
Travis Brown Avatar answered Sep 23 '22 00:09

Travis Brown


Unfortunately shapeless's lenses aren't in much better shape wrt type inference in this case,

scala> import shapeless._ ; import Nat._
import shapeless._
import Nat._

scala> def fst[A, B] = Lens[(A, B)] >> _0
fst: [A, B]=> shapeless.Lens[(A, B),A]

scala> def snd[A, B] = Lens[(A, B)] >> _1
snd: [A, B]=> shapeless.Lens[(A, B),B]

scala> (snd compose fst).set(((1, 2), 3))(9)
<console>:16: error: polymorphic expression cannot be instantiated
  to expected type;
 found   : [A, B]shapeless.Lens[(A, B),A]
 required: shapeless.Lens[?,(?, ?)]
              (snd compose fst).set(((1, 2), 3))(9)

However, if we sprinkle some type annotations,

scala> (snd compose fst[(Int, Int), Int]).set(((1, 2), 3))(9)
res0: ((Int, Int), Int) = ((1,9),3)

The root of the problem, both here and in the scalaz.Lens case, is that what we need are lenses which are both values (so that they can be composed) and polymorphic (so that we can abstract over tuple element types). shapeless and scalaz lenses are values, but not polymorphic (at least, not usefully).

shapeless should be able to do better ... watch this space.

like image 44
Miles Sabin Avatar answered Sep 21 '22 00:09

Miles Sabin