John Threepwood
John Threepwood

Reputation: 16143

Why can a Map object be created without an apply-method?

C:\Users\John>scala
Welcome to Scala version 2.9.2 (Java HotSpot(TM) Client VM, Java 1.6.0_32).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scala.collection.mutable.Map
import scala.collection.mutable.Map

scala> Map()
res4: scala.collection.mutable.Map[Nothing,Nothing] = Map()

When using Map() without keyword new the apply method from the corresponding companion object will be called. But the Scala Documentation does not list an apply method for mutable Maps (only an apply method to retrieve a value from the map is provided).

Why is the code above still working ?

Upvotes: 3

Views: 349

Answers (3)

kiritsuku
kiritsuku

Reputation: 53348

As sepp2k already mentioned in his comment the symbol Map refers to the companion object of Map, which gives you access to its single instance. In patter-matching this is often used to identify a message:

scala> case object Foo
defined module Foo

scala> def send[A](a: A) = a match { case Foo => "got a Foo" case Map => "got a Map" }
send: [A](a: A)String

scala> send(Map)
res8: String = got a Map

scala> send(Foo)
res9: String = got a Foo

If you write Map() you will call the apply method of object Map. Because you did not give any values to insert into the Map the compiler can't infer any type, thus it has to use the bottom type - Nothing - which is a subtype of every type. It is the only possible type to infer which will not break the type system although there are variances. Would Nothing not exist the following code would not compile:

scala> Map(1 -> 1) ++ Map()
res10: scala.collection.mutable.Map[Int,Int] = Map(1 -> 1)

If you take a look to the type signature of ++ which is as follows (source)

def ++[B1 >: B](xs: GenTraversableOnce[(A, B1)]): Map[A, B1]

you will notice the lower-bound type parameter B1 >: B. Because Nothing is a subtype of everything (and B in our case) the compiler can find a B1 (which is Int in our case) and successfully infer a type signature for our Map. This lower-bound is needed because B is covariant (source),

trait MapLike[A, +B, ...] ...

which means that we are not allowed to deliver it as a method parameter (because method parameters are in contravariant position). If the method parameters would not be in contravariant position Liskov's substitution principle would no longer kept by the type system. Thus to get the code to compile a new type (here called B1) has to be found.

As Didier Dupont already pointed out there are some bugs in Scaladoc 2.9, which are solved in 2.10. Not only some missed methods are displayed there but also methods added by an implicit conversion can be displayed (Array for example does display a lot of methods in 2.10 which are not displayed in 2.9).

Upvotes: 1

Dave Griffith
Dave Griffith

Reputation: 20515

There is an apply() method on the companion object of scala.collection.immutable.Map(). It is inherited from scala.collection.MapFactory . That method takes a variable number of pair arguments, and is usually used as

Map("foo"->3, "bar"->4, "barangus"->5)

Calling it with no arguments evidently works as well, but someone brighter than me would have to explain why the type inference engine comes up with scala.collection.mutable.Map[Nothing,Nothing] for it.

Upvotes: 3

Didier Dupont
Didier Dupont

Reputation: 29528

It looks like a bug in scaladoc. There is an apply method in object collection.mutable.Map (inherited from GenMapFactory) but it does not appear in the doc for Map. This problem seems to be fixed in the doc for upcomping 2.10.

Note : you must look into the object documentation, not the class one. The method apply in the class of course works with an existing map instance, and retrieve data from it.

Upvotes: 5

Related Questions