Перегрузка по типу возврата? - PullRequest
10 голосов
/ 02 мая 2010
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)

Из этого примера кажется, что метод map перегружен для возвращаемого типа. Перегрузка по типу возврата невозможна, верно? Кто-нибудь, пожалуйста, объясните, что здесь происходит?

Ответы [ 6 ]

12 голосов
/ 02 мая 2010

map не перегружен при возврате. Вместо этого есть один метод с абстрактным типом возврата.

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

В байт-коде это стирается до Object:

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

Шаблон лучше всего описан в статье Борьба с гнилой битой с типами

5 голосов
/ 03 мая 2010

Это не то, что происходит в этом случае, но на самом деле да, перегрузка для типа возврата поддерживается , поддерживаемым JVM . Это раскрывается в Scala для методов, которые имеют разные общие типы аргументов, которые стираются в один и тот же тип. Пример, приведенный по ссылке:

object Overload{
  def foo(xs : String*) = "foo"; // after erasure is foo:(Seq)String
  def foo(xs : Int*) = 3;        // after erasure is foo:(Seq)Int
}
4 голосов
/ 02 мая 2010

Возможно, вы захотите посмотреть на этот вопрос о сигнатуре map с параметрическим типом возврата That. Ответ Мартина Одерского объясняет, как (и почему) могут возвращаться разные типы.

Обратите внимание, что тип возвращаемого значения никак не связан с до Traversable или даже параметризованным типом цели. Например:

IndexedSeq[Char].map --> String

Как можно увидеть, взглянув на StringOps

3 голосов
/ 06 мая 2010

Хотя правда сложна , я собираюсь продолжить обсуждение, в котором применяется тот же принцип, но многое упрощается, чтобы вы могли видеть логику в языке.

В Scala отсутствует перегрузка в зависимости от типа возвращаемого значения. (Даже в сопоставлении с шаблоном, где «параметры» для шаблона совпадают с типом возврата из unapply, что позволяет использовать тип возвращаемого значения для устранения перегрузки.)

Вы не перегружаете метод map на основе типа возвращаемого значения - вы перегружаете его на основе возвращаемого типа функции, передаваемой в качестве параметра. Изменение типа возвращаемого значения изменяет тип возвращаемого значения параметра, поэтому вы по существу перегружены на основе разных типов параметров. Итак, логически говоря, у вас есть нечто эквивалентное следующей ситуации:

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

Тип возвращаемого значения функции, передаваемой в map, определяет, какая версия вызывается.

Реальная реализация просто делает эту логику более общей и расширяемой для множества типов коллекций, которые есть в библиотеке Scala, и для множества других типов коллекций, которые еще не были написаны.

1 голос
/ 04 августа 2017

С классом типов может быть достигнута перегрузка с другим типом возврата:

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

пример из: https://github.com/cuzfrog/scala-points#29-ad-hoc-overloading-monkey-patch-method-with-different-return-type

0 голосов
/ 03 мая 2010

Вот пример, взятый из игры, которую я пишу. Переопределяет тип возвращаемого значения:

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
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...