Где Скала искать неявные преобразования?
An подразумевается вопрос к новичкам в Scala, кажется, таков: где компилятор ищет импликаты? Я имею в виду неявный, потому что вопрос никогда не кажется полностью сформированным, как будто для него не было слов. :-) Например, где значения integral ниже взялось?
scala> import scala.math._
import scala.math._
scala> def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}
foo: [T](t: T)(implicit integral: scala.math.Integral[T])Unit
scala> foo(0)
scala.math.Numeric$IntIsIntegral$@3dbea611
scala> foo(0L)
scala.math.Numeric$LongIsIntegral$@48c610af
еще один вопрос, который следует за теми, кто решил узнать ответ на первый вопрос, заключается в том, как компилятор выбирает, какой неявный использовать, в некоторых случаях ситуации очевидной двусмысленности (но это все равно компилируется)?
например, scala.Predef определены два преобразования из String: один WrappedString и другое StringOps. Однако оба класса имеют много общих методов, поэтому почему Scala не жалуется на двусмысленность, когда, скажем, вызывает map?
Примечание: этот вопрос был вдохновлен этим другим вопросом, в надежде изложить проблему в более общем виде. Пример был скопирован оттуда, потому что это упоминается в ответе.
2 ответов:
типов неявные преобразования
Implicits в Scala относится либо к значению, которое может быть передано "автоматически", так сказать, или преобразование из одного типа в другой, которое выполняется автоматически.
Неявное Преобразование
говоря очень кратко о последнем типе, если вызвать метод
mна объектеoклассаC, и этот класс не поддерживает методm, тогда Scala будет искать неявное преобразование изCто, что тут поддержкаm. Простым примером может быть методmaponString:"abc".map(_.toInt)
Stringне поддерживает методmap, аStringOpsделает, и есть неявное преобразование изStringдоStringOps(см.implicit def augmentStringonPredef).Неявных Параметров
другой вид неявного является неявным параметр. Они передаются в метод вызовы, как и любой другой параметр, но компилятор пытается заполнить их автоматически. Если он не может, он будет жаловаться. Один can передать эти параметры явно, который является, как один использует
breakOut, например (см. вопрос оbreakOut, в день, когда вы чувствуете себя на вызов).в этом случае нужно объявить о необходимости неявного, такого как
fooспособ декларации:def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}Посмотреть Границы
есть одна ситуация, когда неявное является как неявным преобразованием, так и неявным параметром. Например:
def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value) getIndex("abc", 'a')метод
getIndexможет получить любой объект, пока существует неявное преобразование, доступное из его класса вSeq[T]. Из-за этого, я могу пройтиStringдоgetIndex, и он будет работать.за кулисами компилятор меняет
seq.IndexOf(value)доconv(seq).indexOf(value).это настолько полезно, что есть синтаксический сахар, чтобы написать их. С помощью это синтаксический сахар,
getIndexможно определить так:def getIndex[T, CC <% Seq[T]](seq: CC, value: T) = seq.indexOf(value)этот синтаксический сахар описывается как view bound, вроде верхний предел (
CC <: Seq[Int]) или нижняя граница (T >: Null).Контексте Границ
Другой распространенный шаблон в неявных параметрах является тип класса pattern. Этот шаблон обеспечивает общий интерфейс для классов, которые не объявить их. Он может служить как в качестве мостового шаблона-получение разделения проблем - и в качестве шаблона адаптера.
The
Integralкласс, который вы упомянули, является классическим примером шаблона класса типа. Другим примером стандартной библиотеки Scala являетсяOrdering. Есть библиотека, которая интенсивно использует этот шаблон, называемый Scalaz.вот пример его использования:
def sum[T](list: List[T])(implicit integral: Integral[T]): T = { import integral._ // get the implicits in question into scope list.foldLeft(integral.zero)(_ + _) }есть также синтаксический сахар для него, называемый контексте связанный, который становится менее полезным из-за необходимости ссылаться на неявное. Прямое преобразование этого метода выглядит следующим образом:
def sum[T : Integral](list: List[T]): T = { val integral = implicitly[Integral[T]] import integral._ // get the implicits in question into scope list.foldLeft(integral.zero)(_ + _) }границы контекста более полезны, когда вам просто нужно передать их к другим методам, которые их используют. Например, метод
sortedonSeqнужен неявныйOrdering. Чтобы создать методreverseSort, можно написать:def reverseSort[T : Ordering](seq: Seq[T]) = seq.sorted.reverse, потому что
Ordering[T]был безоговорочно принят вreverseSort, затем он может передайте его неявно вsorted.где неявные преобразования взялось?
когда компилятор видит необходимость в неявном, либо потому, что вы вызываете метод, который не существует в классе объекта, либо потому, что вы вызываете метод, который требует неявного параметра, он будет искать неявный, который будет соответствовать потребности.
этот поиск подчиняться определенным правилам, которые определяют, какие неявные преобразования видны, а какие нет. В следующей таблице показаны где компилятор будет искать импликаты был взят из отличного презентация о последствиях Джоша Суэрета, которые я сердечно рекомендую всем, кто хочет улучшить свои знания Scala. С тех пор он был дополнен обратной связью и обновлениями.
имплициты, доступные под номером 1 ниже, имеют приоритет над теми, которые находятся под номером 2. Кроме того, если существует несколько допустимых аргументов, которые соответствуют типу неявного параметра, то наиболее конкретный будет выбран с использованием правил разрешения статической перегрузки (см. спецификацию Scala §6.26.3). Более подробную информацию можно найти в вопросе, на который я ссылаюсь в конце этого ответа.
- первый взгляд в текущей области
- неявные преобразования, определенные в текущей области
- явного импорта
- импорт подстановочных знаков
такой же объем в другом файлы- теперь посмотрите на связанные типы в
- сопутствующие объекты типа
- неявная область действия типа аргумента (2.9.1)
- неявная область аргументов типа (2.8.0)
- внешние объекты для вложенных типов
- другие размеры
приведем несколько примеров для они:
неявные преобразования, определенные в текущей области
implicit val n: Int = 5 def add(x: Int)(implicit y: Int) = x + y add(5) // takes n from the current scopeЯвного Импорта
import scala.collection.JavaConversions.mapAsScalaMap def env = System.getenv() // Java map val term = env("TERM") // implicit conversion from Java Map to Scala MapИмпорт Подстановочных Знаков
def sum[T : Integral](list: List[T]): T = { val integral = implicitly[Integral[T]] import integral._ // get the implicits in question into scope list.foldLeft(integral.zero)(_ + _) }та же область в других файлах
Edit: похоже, это не имеет другого приоритета. Если у вас есть пример, который демонстрирует различие в приоритете, пожалуйста, сделайте комментарий. В противном случае, не полагайтесь на это.
это похоже на первый пример, но предполагая, что неявное определение находится в другом файле, чем его использование. Смотрите также как объекты может быть использован в привести в неявные преобразования.
сопутствующие объекты типа
здесь есть два объекта-компаньона. Во-первых, рассматривается объект-компаньон типа "источник". Например, внутри объекта
Optionсуществует неявное преобразование кIterable, так что можно назватьIterableметодыOption, или передатьOptionto что-то ожидаяIterable. Например:for { x <- List(1, 2, 3) y <- Some('x') } yield (x, y)это выражение переводится компилятором в
,List(1, 2, 3).flatMap(x => Some('x').map(y => (x, y)))List.flatMapждетTraversableOnce, которыйOptionнет. Затем компилятор заглядывает внутрьOptionобъект компаньона и находит преобразование вIterable, который являетсяTraversableOnce, что делает это выражение правильное.во-вторых, сопутствующий объект ожидаемого типа:
List(1, 2, 3).sortedметод
sortedпринимает неявноеOrdering. В этом случае он смотрит внутрь объектаOrdering, товарищ по классуOrdering, и находит неявноеOrdering[Int]там.обратите внимание, что сопутствующие объекты суперклассов также рассматриваются. Например:
class A(val n: Int) object A { implicit def str(a: A) = "A: %d" format a.n } class B(val x: Int, y: Int) extends A(y) val b = new B(5, 2) val s: String = b // s == "A: 2"вот как скала нашел неявное
Numeric[Int]иNumeric[Long]в твоем вопросе, кстати, как они находятся внутриNumeric, а неIntegral.неявная область действия аргумента Типа
если у вас есть метод с аргументом типа
A, то неявная область типаAтакже будут рассмотрены. Под "неявной областью" я подразумеваю, что все эти правила будут применяться рекурсивно-например, сопутствующий объектAбудет выполнен поиск неявные преобразования, согласно приведенному выше правилу.обратите внимание, что это не означает неявный объем
Aбудет выполнен поиск преобразований этого параметра, но всего выражения. Для пример:class A(val n: Int) { def +(other: A) = new A(n + other.n) } object A { implicit def fromInt(n: Int) = new A(n) } // This becomes possible: 1 + new A(1) // because it is converted into this: A.fromInt(1) + new A(1)это доступно с Scala 2.9.1.
неявная область аргументов типа
это необходимо для того, чтобы шаблон класса типа действительно работал. Рассмотрим
Ordering, например: он поставляется с Некоторые неявные преобразования в объект-компаньон, но вы не можете добавить материал. Так как же вы можете сделатьOrderingдля вашего собственного класса, который автоматически найден?давайте начнем с реализация:
class A(val n: Int) object A { implicit val ord = new Ordering[A] { def compare(x: A, y: A) = implicitly[Ordering[Int]].compare(x.n, y.n) } }Итак, рассмотрим, что происходит, когда вы называете
List(new A(5), new A(2)).sortedкак мы видели, метод
sortedждетOrdering[A](на самом деле, он ожидаетOrdering[B], гдеB >: A). Внутри нет ничего подобногоOrdering, и нет типа "источник", на который можно смотреть. Очевидно, что он находит его внутриA, который является аргумент типа наOrdering.это также, как различные методы сбора ожидая
CanBuildFromработа: импликаты находятся внутри сопутствующих объектов к параметрам типаCanBuildFrom.Примечание:
Orderingопределяется какtrait Ordering[T], гдеTпараметр типа. Ранее я сказал, что Scala посмотрел внутри параметров типа, что не имеет большого смысла. Неявный искал вышеOrdering[A], гдеAявляется фактическим типом, а не параметром типа: это аргумент типа toOrdering. См. раздел 7.2 Scala спецификация.это доступно с Scala 2.8.0.
внешние объекты для вложенных типов
я на самом деле не видел примеров этого. Я был бы благодарен, если бы кто-то мог поделиться им. Принцип прост:
class A(val n: Int) { class B(val m: Int) { require(m < n) } } object A { implicit def bToString(b: A#B) = "B: %d" format b.m } val a = new A(5) val b = new a.B(3) val s: String = b // s == "B: 3"Другие Размеры
я почти уверен, что это была шутка, но этот ответ может быть не последним. Поэтому не принимайте этот вопрос как окончательный арбитр того, что происходит, и если вы заметили, что он устарел, пожалуйста, сообщите мне, чтобы я мог это исправить.
EDIT
связанные вопросы, представляющие интерес:
Я хотел узнать приоритет разрешения неявного параметра, а не только там, где он ищет, поэтому я написал сообщение в блоге пересмотр последствий без налога на импорт (и неявный приоритет параметров снова после некоторой обратной связи).
вот список:
- 1) имплициты, видимые для текущей области вызова через локальное объявление, импорт, внешнюю область, наследование, объект пакета, которые доступны без префикс.
- 2) неявная область, который содержит все виды сопутствующих объектов и объекта пакета, которые имеют некоторое отношение к типу неявного, который мы ищем (т. е. объект пакета типа, сопутствующий объект самого типа, его конструктор типа, если таковой имеется, его параметры, если таковые имеются, а также его супертип и супертраиты).
Если на любом этапе мы находим более одного неявного, статическое правило перегрузки используется для разрешения оно.
Comments