Tutorial :Overloading on return type?



Question:

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?


Solution:1

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


Solution:2

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  }  


Solution:3

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


Solution:4

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.


Solution:5

With typeclass, overloading with different return type could be achieved:

object AdHocOverloading extends App {      implicit class UnboxOps[T, R, B](b: B)(implicit ev: UnboxEv[T, B, R], ev1: B <:< Box[T]) {      def value: R = ev.unbox(b)    }      val optional = Box(Some(3))    val confident = new Box(Some("C")) with Confidence    val otherType = Seq("bad")      optional.value    confident.value      //otherType.value //compile time error      println(optional.value)    //Some(3)    println(confident.value)    //C  }    trait UnboxEv[+T, -B, +R] {    def unbox(b: B): R  }    trait Confidence  case class Box[+T](v: Option[T]) //v could be private  trait LowLevelImplicitOfBox {    this: Box.type =>    implicit def optionEvidence[T]: UnboxEv[T, Box[T], Option[T]] =      new UnboxEv[T, Box[T], Option[T]] {        override def unbox(b: Box[T]): Option[T] = b.v      }  }  object Box extends LowLevelImplicitOfBox {    implicit def confidentEvidence[T]: UnboxEv[T, Box[T] with Confidence, T] =      new UnboxEv[T, Box[T] with Confidence, T] {        override def unbox(b: Box[T] with Confidence): T = b.v.get      }  }  

example from:https://github.com/cuzfrog/scala-points#29-ad-hoc-overloading-monkey-patch-method-with-different-return-type


Solution:6

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      }  }  

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »