tags:

views:

194

answers:

5
scala> val shares = Map("Apple" -> 23, "MicroSoft" -> 50, "IBM" -> 17)
shares: scala.collection.immutable.Map[java.lang.String,Int] 
      = Map(Apple -> 23, MicroSoft -> 50, IBM -> 17)

scala> val shareholders = shares map {_._1}                           
shareholders: scala.collection.immutable.Iterable[java.lang.String] 
            = List(Apple, MicroSoft, IBM)

scala> val newShares = shares map {case(k, v) => (k, 1.5 * v)}     
newShares: scala.collection.immutable.Map[java.lang.String,Double] 
         = Map(Apple -> 34.5, MicroSoft -> 75.0, IBM -> 25.5)

From this example it seems like the map method is overloaded on return type. Overloading on return type is not possible right? Would somebody please explain what's going on here?

+8  A: 

map isn't overloaded on return type. Instead, there is one method with an abstract return type.

def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That

In the bytecode, this is erased to Object:

public abstract java.lang.Object map(scala.Function1, scala.collection.generic.CanBuildFrom);

The pattern is described best in the paper Fighting Bit Rot with Types

retronym
This is the scariest thing I have ever seen since I started programming.
Green Hyena
It's hard to write this `map` method, but generally very easy to use.
retronym
+3  A: 

You might want to look at this question about the signature of map which has a parametric return type That. Martin Odersky's answer explains how (and why) different types may be returned.

Note that the return type is not bound in any way to Traversable, or even the parametrized type of the target. For example:

IndexedSeq[Char].map --> String

As can be seen by looking at StringOps

oxbow_lakes
Thanks for the link. Didn't understand anything though. Will check that thread again after a few months, when I'd hopefully have become a better scalar. :-)
Green Hyena
+2  A: 

This isn't what's going on in this case, but actually yes, overloading on return type is supported by JVM. This is exposed in Scala for methods which have different generic argument types which erase to the same type. The example given at the link is

object Overload{
  def foo(xs : String*) = "foo"; // after erasure is foo:(Seq)String
  def foo(xs : Int*) = 3;        // after erasure is foo:(Seq)Int
}
Alexey Romanov
A: 

Here's an example taken from a game I'm writing. It overrides the return type:

def consumeItem(item: ConsumableItem) {
    executePartyAction[Unit](_.inventory.consumeItem(item, this))
}

def craftItem[ItemType <: Item](recipe: Recipe[ItemType]) = {
    executePartyAction[ItemType](_.inventory.craftItem(recipe, this))
}

private def executePartyAction[ReturnType](partyAction: Party => ReturnType): ReturnType = {
    party match {
        case Some(party) => partyAction(party)
        case None => throw new PlayerCharacterMustBelongToAParty
    }
}
egervari
This doesn't overload a method based on the method's return type. It overloads a function based on the return type of a function that's passed to the method -- that is, the parameter type of the method itself.
Ken Bloom
+2  A: 

While the truth is complicated, I'm going to contibute a discussion that applies the same principle but simplifies things a lot so you can see the logic in the language.

Scala doesn't have overloading based on return type. (Not even in pattern matching where the "parameters" to the pattern match up with the return type of the unapply, making it possible to use the return type to resolve the overload.)

You're not overloading the map method based on return type -- you're overloading it based on the return type of function that's passed as a parameter. The change in return type changes the return type of the parameter, so you're essentially overloading based on different paramter types. So logically speaking, you have something equivalent to the following situation:

trait Seq[X]{
  def map[Y](func: X => Y) : Seq[Y]
  def map[Y,Z](func: X => Tuple2[Y,Z]) : Map[Y,Z]
}

The return type of the function passed to map determines which version is called.

The real implementation just makes this logic more general and extensible to lots of collection types that are in the Scala library, and to lots of other collection types that haven't been written yet.

Ken Bloom