Reputation: 15811
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—e.g. when returning a collection from a function which uses a mutable list to build up a result, but returns an immutable value.
Upvotes: 5
Views: 350
Reputation: 8249
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 (i.e. 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.)
Upvotes: 2
Reputation: 44908
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)
Upvotes: 6