Hi,
I am trying to understand possible negative consequences of using `@uncheckedVariance`.
I am looking at TailSwitch. Both `I` and `O` types (that are in contravariant and covariant positions respectively) are all used same positions in different usages of TailSwitch instances: `i: TailSwitch[I2, O, I]` and `o: TailSwitch[O, I2, O2]`. And to avoid code duplications TailSwitch just drops variance check. Is that the reason of using `@uncheckedVariance`?
If yes, then why `Lifter` drops variance check?
--------------
Another question that caused `@uncheckedVariance` is that `I` contravariance and `O` covariance in the `Rule` definition:
That canonical Scala’s `Function`-fashion implementation states that, for example having
class Publication(val title: String)
class Book(title: String) extends Publication(title)
It is true that:
scala> implicitly[Rule[Publication :: HNil, String :: HNil] <:< Rule[Book :: HNil, AnyRef :: HNil]]
res4: <:<[org.parboiled2.Rule[shapeless.::[org.parboiled2.examples.Publication,shapeless.HNil],shapeless.::[String,shapeless.HNil]],org.parboiled2.Rule[shapeless.::[org.parboiled2.examples.Book,shapeless.HNil],shapeless.::[AnyRef,shapeless.HNil]]] = <function1>
A `Rule` instance can’t be passed as argument (or can it?) being subtype of some other `Rule`. Why then parboiled2 need that type variance?
--------------
Also, let’s define some rules:
def r1: Rule[Publication :: HNil, String :: HNil] = rule { MATCH ~> { (p: Publication) => p.title } }
def r2: Rule[Book :: HNil, AnyRef :: HNil] = rule { MATCH ~> { (b: Book) => List(b.title) } }
def book: Rule[HNil, Book :: HNil] = rule { capture("b") ~> { (s: String) => new Book(s) } }
def pub: Rule[HNil, Publication :: HNil] = rule { capture("b") ~> { (s: String) => new Publication(s) } }
def test1 = rule { book ~ r2 }
def test2 = rule { pub ~ r2 }
def test3 = rule { book ~ r1 }
def test4 = rule { pub ~ r1 }
def test5 = rule { book ~ (r1 | r2) }
def test6 = rule { book ~ (r2 | r1) }
def test7 = rule { pub ~ (r1 | r2) }
def test8 = rule { pub ~ (r2 | r1) }
`test2` should work, but fails at runtime with cast exception.
`test3` should not pass type check, but works brilliantly in runtime.
`test{5..8}` pass type check to runtime, where those give weird results.
Type check does not save us much here, doesn’t it?