简体   繁体   中英

Scala: const method in covariant class

Suppose I want to implement some sort of immutable set in scala and I want it to have 2 methods -- add and contains. Also it seems logical to me that a Set[Cat] is a set of Set[Animals] (thus I want it to be declated as MySet[+A] ).

So I write something like this

class MySet[+A] {
    def add[B >: A](elem: B): MySet[B]
}

Which seems to be ok until I try to implement contains method. In my opinion, the best fitting signature for a method like it should be def contains(elem: A): Boolean because a set of Cat s can only contain Cat s. It can not contain Dog s or any other animals.

Of course scala complains about the method like this: Covariant type A occurs in a contravariant position in type A of value elem and I even understand why.

My question is how to convince scala that my method contains does not mutate my collection in any way and it is safe to pass an element of type A into it.

The problem is not that scala is afraid you are going to mutate the collection.

The problem is that, because your set is covariant, MySet[Cat] is a subclass of MySet[Animal] . So, if the latter is allowed to have def contains(a: Animal) , the former must have one too.

You can make it work with the same trick you did with .add :

 def contains[B >: A](elem: B): Boolean

Note that the immutable Set in standard scala library is actually invariant in the element type. That is because they wanted it to work as a function A => Boolean , that would require it to have an apply(a: A) that's essentially the same problem you are talking about.

And give back my nick BTW, this is not cool, man!

If for typesafe contains , you can try to create the typesafe implicits for contains to solve Set 's contains method parameter must be cotravariant problem, like:

  //implicit bound for `MySet` with the type `A`, so in there `A` is invariant type. 
  implicit class TypeSafeMySetContains[A](s: MySet[A]) {
    def has(a: A): Boolean = s.contains(a) // invoke the `contains` method check
  }
  MySet(1, 2, 3) has "3" // the compiler will throw type mismatch error
  MySet(1, 2, 3) has 3

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM