Scala 2.8 CanBuildFrom

Следуя другому вопросу, я спросил, Scala 2.8 breakout, я хотел немного подробнее узнать о методе Scala TraversableLike[A].map, подпись которого следующим образом:

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

Обратите внимание на несколько вещей об этом методе:

  • Требуется функция, поворачивающая каждый A в проходящем через B.
  • Он возвращает That и принимает неявный аргумент типа CanBuildFrom[Repr, B, That].

Я могу назвать это следующим образом:

> val s: Set[Int] = List("Paris", "London").map(_.length)
s: Set[Int] Set(5,6)

Что Я не могу понять, как тот факт, что That привязан к B (то есть, это некоторая коллекция B), принудительно выполняется компилятором. Параметры типа выглядят независимыми как от сигнатуры выше, так и от сигнатуры самого признака CanBuildFrom:

trait CanBuildFrom[-From, -Elem, +To]

Как компилятор Scala гарантирует, что That не может быть принужден к чему-то, что не имеет смысла?

> val s: Set[String] = List("Paris", "London").map(_.length) //will not compile

Как компилятор решает, какие неявные объекты CanBuildFrom находятся в области для вызова?

Ответ 1

Обратите внимание, что второй аргумент map является неявным аргументом. Там должен быть неявным в области с соответствующими типами или, иначе, вы должны передавать такой аргумент.

В вашем примере That должен быть Set[String], B должен быть Int и Repr должен быть List[String]. Поэтому для компиляции вам нужен следующий неявный объект в области видимости:

implicit object X: CanBuildFrom[List[String], Int, Set[String]]

В сфере нет такой вещи. Кроме того, breakOut не может предоставить его, потому что сам он нуждается в неявном CanBuildFrom, первым типом которого может быть любой класс (контравариантный потомок Nothing), но в противном случае он ограничен другими типами.

Посмотрите, например, на CanBuildFrom factory из сопутствующего объекта List:

implicit def  canBuildFrom [A] : CanBuildFrom[List, A, List[A]]  

Поскольку он связывает второй и третий параметры с помощью A, подразумеваемый вопрос не будет работать.

Итак, как узнать, где искать, в отношении таких имплицитов? Прежде всего, Scala импортирует несколько вещей во все области. Сейчас я могу вспомнить следующие импорты:

import scala.package._ // Package object
import scala.Predef._  // Object
// import scala.LowPriorityImplicits, class inherited by Predef
import scala.runtime._ // Package

Поскольку мы обеспокоены имплицитами, обратите внимание, что при импорте вещей из пакетов единственными возможными могут быть синглтоны. С другой стороны, когда вы импортируете объекты из объектов (одноточие), вы можете иметь неявные определения, значения и одиночные числа.

Прямо сейчас есть CanBuildFrom implicits внутри Predef и LowPriorityImplicits, которые связаны со строками. Они позволяют нам писать "this is a string" map (_.toInt).

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

Я говорю сопутствующие объекты во множественном числе, потому что сопутствующие объекты всех признаков и классов, унаследованные классом рассматриваемого экземпляра, могут содержать соответствующие импликации. Я не уверен, что сам экземпляр может содержать неявный. Честно говоря, я не могу воспроизвести это прямо сейчас, поэтому я, конечно, что-то ошибаюсь.

Во всяком случае, загляните внутрь объектов-компаньонов.

Ответ 2

object ArrayBuffer extends SeqFactory[ArrayBuffer] {
  /** $genericCanBuildFromInfo */
  implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, ArrayBuffer[A]] = new GenericCanBuildFrom[A]
  def newBuilder[A]: Builder[A, ArrayBuffer[A]] = new ArrayBuffer[A]
}