Reputation: 245
I've been using scala for a while now and I thought I was really starting to understand everything (well, most things...), but I found myself confused by a number of the method definitions in the Map class. I know how foldLeft, etc work, but what I'm confused about is the type parameters used in the Map functions. Let's use foldLeft as an example:
foldLeft [B] (z: B)(op: (B, (A, B)) ⇒ B) : B
The definition for the Map trait itself takes two type parameters 'A' and 'B' (e.g. Map[A,+B]). From what I can tell, when you define a type parameter for a method using the same name as one of the type parameters for the class/trait, it overrides the class/trait value. Take this definition of Foo as an example:
class Foo[A : Manifest, B : Manifest] {
def isAString() = manifest[A] == manifest[String]
def isAInt() = manifest[A] == manifest[Int]
def isBString() = manifest[B] == manifest[String]
def isBInt() = manifest[B] == manifest[Int]
def nowIsBString[B : Manifest] = manifest[B] == manifest[String]
}
scala> val f = new Foo[String,Int]
f: Foo[String,Int] = Foo@7bacb41
scala> f.isAString
res290: Boolean = true
scala> f.isAInt
res291: Boolean = false
scala> f.isBString
res292: Boolean = false
scala> f.isBInt
res293: Boolean = true
scala> f.nowIsBString[String]
res294: Boolean = true
scala> f.nowIsBString[Int]
res295: Boolean = false
So in the foldLeft definition, 'B' comes from the method definition and 'A' comes from the trait definition. For example:
val xm = Map("test" -> 1, "test2" -> 2)
scala> val foldFn = (z: Int, kv: (String, Int)) => z + kv._2
foldFn: (Int, (String, Int)) => Int = <function2>
scala> m.foldLeft(0)(foldFn)
res298: Int = 3
This is as expected as 'B' for the function matches 'B' for the trait, but what if I change the 'B' type for the function to String instead of Int:
scala> val foldFn = (z: String, kv: (String, String)) => z + kv._2
foldFn: (String, (String, String)) => java.lang.String = <function2>
scala> m.foldLeft("")(foldFn)
<console>:19: error: type mismatch;
found : (String, (String, String)) => java.lang.String
required: (java.lang.String, (java.lang.String, Int)) => java.lang.String
m.foldLeft("")(foldFn)
So let's change the kv parameter to (String, Int):
scala> val foldFn = (z: String, kv: (String, Int)) => z + kv._2
foldFn: (String, (String, Int)) => java.lang.String = <function2>
scala> m.foldLeft("")(foldFn)
res299: java.lang.String = 12
Unlike my Foo example, in this case the Map's 'B' value is taking precedence over the functions definition, but only for the kv parameter. What I would have expected is to see a foldLeft defined as follows:
foldLeft[C] (z: C)(op: (C, (A, B)) => C): C
That would be more clear to me, but it is not defined this way. So does anyone know the rules for when a methods parameter will override a trait/class parameter and when it will not?
Upvotes: 2
Views: 1734
Reputation: 139028
Scala is the same as Java in this respect, and the following from the "Names" chapter of the Java specification applies:
A declaration d of a type named n shadows the declarations of any other types named n that are in scope at the point where d occurs throughout the scope of d.
So the type parameter for a method will always shadow a class or trait type parameter with the same name. Your Foo
example demonstrates this fact.
The apparent counterexample you're seeing in the case of Map
's foldLeft
is just an unpleasant artifact of the current version of Scaladoc, as is pointed out in the answers to the question you've linked. foldLeft
isn't defined in the Map
trait, but in TraversableOnce
, where there isn't a trait type parameter named B
at all.
In general shadowing the type parameter of a trait or class in a method is a really bad idea, of course.
Upvotes: 1