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]
}
Другие вопросы по тегам