10
votes

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?

2

2 Answers

9
votes

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.

6
votes

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.