In another question, I’m advised to use with in a place where one normally uses <: or <:<. So instead of defining functions in either of the following two ways:
scala> def f[A,C <: Seq[A]](xs: C) = 0
f: [A, C <: scala.collection.immutable.Seq[A]](xs: C)Int
scala> f(List(1))
<console>:54: error: inferred type arguments [Nothing,List[Int]] do not conform to method f's type parameter bounds [A,C <: scala.collection.immutable.Seq[A]]
f(List(1))
^
scala> implicit def f[A,C](xs: C)(implicit ev: C <:< Seq[A]) = new { def foo = 0 }
f: [A, C](xs: C)(implicit ev: <:<[C,scala.collection.immutable.Seq[A]])java.lang.Object{def foo: Int}
scala> List(0) foo
<console>:54: error: Cannot prove that List[Int] <:< scala.collection.immutable.Seq[A].
List(0) foo
^
scala> f(List(0)) foo
res17: Int = 0
One can do:
scala> implicit def f[A,C](xs: C with Seq[A]) = new { def foo = 0 }
f: [A, C](xs: C with scala.collection.immutable.Seq[A])java.lang.Object{def foo: Int}
scala> List(0) foo
res18: Int = 0
My question is: besides the above particular case, when should one use with instead of <: or <:< on the type parameter? Why not always use with instead? I’m looking for a discussion of the nuances among the alternatives here. Thanks.
The meanings are entirely different.
C <: Seq[A]means thatCis a subtype ofSeq[A], as you know;xs: C with Seq[A]doesn’t put any bound onC, but means thatxsshould be both aCand aSeq[A]. Therefore you should normally use the one you actually mean.In
def f[A,C <: Seq[A]](xs: C)the problem is that Scala’s compiler can’t inferAbecause it doesn’t appear explicitly in the type of arguments. I don’t see any reason in principle it couldn’t inferA; it just doesn’t currently. Replacing the type withC with Seq[A]meansAnow appears in the type ofxsand allows the compiler to inferA. So if you really mean the bound, butAto be inferred, you actually need to writeinstead of your third definition, and this is what the answer to the linked question does.