Scala 2.8 CanBuildFrom
Исходя из другого вопроса, который я задал, прорыв Scala 2.8, я хотел бы немного больше понять метод 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
объекты находятся в поле зрения для вызова?
2 ответа
Обратите внимание, что второй аргумент map
является неявным аргументом. Должен быть неявный в области видимости с соответствующими типами, или, в противном случае, вы должны передать такой аргумент.
В вашем примере That
должно быть Set[String]
, B должен быть Int
а также Repr
должно быть List[String]
, Следовательно, для этого вам необходим следующий неявный объект в области видимости:
implicit object X: CanBuildFrom[List[String], Int, Set[String]]
Там нет такой вещи в объеме. Также, breakOut
не может предоставить это, потому что само по себе нуждается в неявном CanBuildFrom
, чей первый тип может быть любым классом (противоположный вариант потомка Nothing
), но в остальном ограничено другими типами.
Взгляните, например, на CanBuildFrom
фабрика из сопутствующего объекта 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
имплицит внутри Predef
а также LowPriorityImplicits
, которые касаются строк. Они позволяют нам писать "this is a string" map (_.toInt)
,
Итак, за исключением автоматического импорта и выполняемых вами явных импортов, где еще можно найти неявный? Одно место: сопутствующие объекты экземпляра, к которому применяется метод.
Я говорю сопутствующий объектs во множественном числе, потому что сопутствующие объекты всех признаков и классов, унаследованных классом рассматриваемого экземпляра, могут содержать соответствующие импликации. Я не уверен, что сам экземпляр может содержать неявный. Если честно, я не могу сейчас воспроизвести это, поэтому я, безусловно, совершаю здесь какую-то ошибку.
В любом случае, загляните внутрь сопутствующих объектов.
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]
}