Search code examples
scaladictionaryhashmapscalazliskov-substitution-principle

Why are HashMaps not Semigroups, but Maps are?


def foo(a: Map[String, Int], b: HashMap[String, Int]) {
  // okay
  val ab = a |+| b

  // value |+| is not a member of scala.collection.immutable.HashMap[String,Int]
  val ba = b |+| a
}

Why are HashMaps not Semigroups, but Maps are? Coming from an object-oriented background, I would have expected that a HashMap is every bit as capable as a Map?


Solution

  • Because Semigroup is invariant and there is no instance defined for specifically HashMap, just Map. The invariance basically means that the |+| syntax cannot use the Semigroup instance for Map on a type which is inferred to be HashMap, even though HashMap is a subtype of Map.

    In some cases, Scalaz typeclasses are needlessly invariant. This is not one of those cases though. Because of the signature of the |+| function, variance (either co- or contra-) wouldn't make much sense, and thus the typeclass is correct in its maximal generality.