简体   繁体   中英

Why doesn't mutable.Map extend immutable.Map?

This code works:

@ mutable.Seq(1, 2).asInstanceOf[Seq[Int]]
res1: Seq[Int] = ArrayBuffer(1, 2)

But this doesn't:

@ mutable.Map(1 -> 2).asInstanceOf[Map[Int, Int]]
java.lang.ClassCastException: scala.collection.mutable.HashMap cannot be cast
to scala.collection.immutable.Map
  ammonite.$sess.cmd1$.<init>(cmd1.sc:1)
  ammonite.$sess.cmd1$.<clinit>(cmd1.sc)

Why can mutable.Seq be viewed as immutable, but not mutable.Map ? I understand that casting a mutable Seq to an immutable one is "lying" about the mutability of the underlying collection, but in some situations the programmer knows better—eg when returning a collection from a function which uses a mutable list to build up a result, but returns an immutable value.

The default Map is defined in Predef as

type Map[A, +B] = collection.immutable.Map[A, B]

so it is explicitly immutable , and mutable.Map is not a subclass of it.

In contrast to that, the default Seq is defined directly in scala as

type Seq[+A] = scala.collection.Seq[A] 

so it is a supertype of both mutable.Seq and immutable.Seq . This is why your first asInstanceOf does not fail: every mutable.Seq is also a collection.Seq .

As explained here , the decision to not specify whether Seq has to be mutable or immutable had something to do with support for arrays and varargs.

In 2.13, the default Seq will become immutable , and a new type ImmutableArray will be introduced to deal with varargs . (Thanks @SethTisue for pointing it out)

The primary problem is this:

If scala.collection.mutable.Map was a subclass of scala.collection.immutable.Map , then the former is-a latter too. That is, a mutable Map is also immutable . Does that make sense?

To illustrate this, you could pass an instance of a mutable Map to a function or constructor expecting an immutable Map . Alas, the two types have different semantics: if you, say, add an element to the immutable version, you'll get a new immutable Map instance returned; yet if you add an element to the mutable version, it changes that instance's contents—thus, it will have a side-effect .

As a consequence, if you wanted to write a pure, referentially transparent ( RT ) function (ie one that has no side-effects ) that takes an immutable Map argument, you couldn't achieve your goal—anyone could screw that up by passing it a mutable Map instance instead. This would then change the meaning of your code and potentially cause all manner of problems.

In functional programming , immutability is big deal, as is RT . By ensuring that the two cannot be confused, programs that need immutable Map s can guarantee that they will get them.

(Of course, if you explicitly want to write code that will accept either, you could request an instance of their common scala.collection.Map trait instead.)

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