Search code examples
scalatypestype-constraints

How to define a scala method with type param that cannot be Any


In below example, I want to define a contains method that doesn't compile if a and b are not of the same base type.

  • In contains1 impl, if a is Seq[Int] and b is String, T is derived to be Any, and it compiles. This is not I want.
  • In contains2 impl, if a is Seq[Int] and b is String, then it doesn't compile. The behavior is what I want.
def contains1[T](a: Seq[T], b: T): Boolean = a.contains(b)

println(contains1(Seq(1,2,3), "four")) // false

def contains2[T: Ordering](a: Seq[T], b: T): Boolean = a.contains(b)

println(contains2(Seq(1,2,3), "four")) // compilation error
// cmd7.sc:1: No implicit Ordering defined for Any.
// val res7 = isMatched(Seq(1,2,3), "s")
                    ^
// Compilation Failed

However, is there a simpler way to achieve the same behaviour as in contains2? Ordering context bound confuses me as the method has nothing to do with sorting/ordering at all.


Solution

  • You could use generalized type constraints operator =:=.

    For example:

    def contains[A,B](a: Seq[A], b: B)(implicit evidence: A =:= B): Boolean = a.contains(b)
    

    and then:

    println(contains1(Seq(1,2,3), "four")) //fails with Cannot prove that Int =:= String.
    println(contains1(Seq("one"), "four")) //returns false
    println(contains1(Seq("one", "four"), "four")) //true
    

    More on generalized type constraints here and here.

    As LuisMiguelMejíaSuárez noticed, you could also consider using B <:< A instead of A =:= B. I won't elaborate on differences between these two because it's described in linked answer and article, but in brief, <:< would also allow all B that are a subtype of A, while =:= needs types to match exactly.