Search code examples
scalagenericstypestype-conversionimplicit

Why Does This Type Constraint Fail for List[Seq[AnyVal or String]]


I am learning about Scala on my own and ran into this. Following on from the excellent answers at Link, suppose I have the following code:

object Example extends App {
  val x = Seq(1, 2, 3)
  val y = Seq("1", "2", "3")

  class Or[A, B]
  implicit def orA[A, B](implicit ev: A): Or[A, B] = new Or
  implicit def orB[A, B](implicit ev: B): Or[A, B] = new Or

  def f1[T](seq: Seq[T])(implicit ev: Or[T =:= Int, T =:= String]) = {
    println(seq)
  }

  f1(Seq(1, 2, 3))
  f1(Seq("1", "2", "3"))
}

This compiles all well and good. But now lets suppose I change the function so that it takes a List of Sequences, instead of just Sequences, and try the following:

object Example extends App {
  val x = Seq(1, 2, 3)
  val y = Seq("1", "2", "3")

  class Or[A, B]
  implicit def orA[A, B](implicit ev: A): Or[A, B] = new Or
  implicit def orB[A, B](implicit ev: B): Or[A, B] = new Or

  def f1[T](seq: List[Seq[T]])(implicit ev: Or[T =:= Int, T =:= String]) = {
    println(seq)
  }

   f1(List(Seq(1, 2, 3), Seq("1", "2", "3")))
}

This however fails. The error message is:

could not find implicit value for parameter ev: conusviz.Example.Or[Any =:= Int,Any =:= String]

My question is, why is this happening? I simply wrapped something the compiler should be able to infer in another type. Is there a way to get this working?

I want to the function to take a List of Int (or Indeed AnyVal) or Strings and still work. I am just a little baffled as to why the compiler is confused. Any explanation along a coded answer would be deeply appreciated from a learning perspective.


Solution

  • Seq(1, 2, 3) has type Seq[Int], Seq("1", "2", "3") has type Seq[String]. Both Seq[Int] and Seq[String] are subtypes of Seq[Any]. So List(Seq(1, 2, 3), Seq("1", "2", "3")) has type List[Seq[Any]].

    If you want types Seq[Int] and Seq[String] to be preserved then you need not a List but HList

    import shapeless.{HList, HNil}
    import shapeless.ops.hlist.LiftAll
    
    def f[L <: HList](seq: L)(implicit ev: LiftAll[({type l[T] = Or[T =:= Seq[Int], T =:= Seq[String]]})#l, L]) = {
      println(seq)
    }
    
    f(Seq(1, 2, 3) :: Seq("1", "2", "3") :: HNil)