Что такое идентификатор Scala «неявно»?


169

Я видел функцию с именем implicitlyиспользуется в примерах Scala. Что это такое и как оно используется?

Пример здесь :

scala> sealed trait Foo[T] { def apply(list : List[T]) : Unit }; object Foo {
     |                         implicit def stringImpl = new Foo[String] {
     |                             def apply(list : List[String]) = println("String")
     |                         }
     |                         implicit def intImpl = new Foo[Int] {
     |                             def apply(list : List[Int]) =  println("Int")
     |                         }
     |                     } ; def foo[A : Foo](x : List[A]) = implicitly[Foo[A]].apply(x)
defined trait Foo
defined module Foo
foo: [A](x: List[A])(implicit evidence$1: Foo[A])Unit

scala> foo(1)
<console>:8: error: type mismatch;
 found   : Int(1)
 required: List[?]
       foo(1)
           ^
scala> foo(List(1,2,3))
Int
scala> foo(List("a","b","c"))
String
scala> foo(List(1.0))
<console>:8: error: could not find implicit value for evidence parameter of type
 Foo[Double]
       foo(List(1.0))
          ^

Обратите внимание, что мы должны писать, implicitly[Foo[A]].apply(x)так как компилятор считает, что это implicitly[Foo[A]](x)означает, что мы вызываем implicitlyс параметрами.

Также см. Как исследовать объекты / типы / и т.д. из Scala REPL? и где Scala ищет последствия?

Ответы:


206

Вот несколько причин использовать восхитительно простой метод implicitly.

Чтобы понять / устранить неполадки неявных представлений

Неявное представление может быть запущено, когда префикс выбора (например, the.prefix.selection(args)не содержит члена, selectionкоторый применим к args(даже после попытки преобразования argsс неявными представлениями). В этом случае компилятор ищет неявные члены, локально определенные в текущей или вложенной области видимости, наследуемой или импортированной, которые являются либо функциями от типа этого the.prefixк типу с selectionопределенными, либо эквивалентными неявными методами.

scala> 1.min(2) // Int doesn't have min defined, where did that come from?                                   
res21: Int = 1

scala> implicitly[Int => { def min(i: Int): Any }]
res22: (Int) => AnyRef{def min(i: Int): Any} = <function1>

scala> res22(1) // 
res23: AnyRef{def min(i: Int): Int} = 1

scala> .getClass
res24: java.lang.Class[_] = class scala.runtime.RichInt

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

scala> 1: scala.runtime.RichInt
res25: scala.runtime.RichInt = 1

Здесь компилятор ищет эту функцию:

scala> implicitly[Int => scala.runtime.RichInt]
res26: (Int) => scala.runtime.RichInt = <function1>

Доступ к неявному параметру, введенному ограниченным контекстом

Неявные параметры, возможно, являются более важной особенностью Scala, чем неявные представления. Они поддерживают шаблон класса типа. Стандартная библиотека использует это в нескольких местах - см.scala.Ordering и как это используется SeqLike#sorted. Неявные параметры также используются для передачи манифестов и CanBuildFromэкземпляров массива .

Scala 2.8 допускает сокращенный синтаксис для неявных параметров, называемый контекстными границами. Вкратце, метод с параметром типа, Aкоторый требует неявного параметра типа M[A]:

def foo[A](implicit ma: M[A])

можно переписать как:

def foo[A: M]

Но какой смысл передавать неявный параметр, а не называть его? Как это может быть полезно при реализации методаfoo ?

Часто на неявный параметр не нужно ссылаться напрямую, он будет туннелироваться как неявный аргумент для другого вызываемого метода. Если это необходимо, вы все равно можете сохранить краткую подпись метода с привязкой к контексту и вызвать implicitlyдля материализации значения:

def foo[A: M] = {
   val ma = implicitly[M[A]]
}

Передача подмножества неявных параметров в явном виде

Предположим, вы вызываете метод, который довольно печатает человека, используя подход на основе классов типов:

trait Show[T] { def show(t: T): String }
object Show {
  implicit def IntShow: Show[Int] = new Show[Int] { def show(i: Int) = i.toString }
  implicit def StringShow: Show[String] = new Show[String] { def show(s: String) = s }

  def ShoutyStringShow: Show[String] = new Show[String] { def show(s: String) = s.toUpperCase }
}

case class Person(name: String, age: Int)
object Person {
  implicit def PersonShow(implicit si: Show[Int], ss: Show[String]): Show[Person] = new Show[Person] {
    def show(p: Person) = "Person(name=" + ss.show(p.name) + ", age=" + si.show(p.age) + ")"
  }
}

val p = Person("bob", 25)
implicitly[Show[Person]].show(p)

Что если мы хотим изменить способ вывода имени? Мы можем явно вызвать PersonShow, явно передать альтернативу Show[String], но мы хотим, чтобы компилятор передал Show[Int].

Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)

2
scala> 1.min (2) res0: Int = 1 В Scala 2.10.3 я получаю ошибку: scala> неявно [Int => {def min (i: Int): Any}] <console>: 8: error: Неявное представление не доступно из Int => AnyRef {def min (i: Int): Any}. неявно [Int => {def min (i: Int): Any}]
jhegedus

Этот ответ будет обновлен до последней версии.
14:58

1
неявно [Int => AnyVal {def min (i: Int): Int}] будет работать. Должно быть исправлено в ответе.
Малкавиано

212

Implicitlyдоступна в Scala 2.8 и определяется в Predef как:

def implicitly[T](implicit e: T): T = e

Обычно используется для проверки доступности неявного значения типа Tи его возврата если это так.

Простой пример из презентации ретронима :

scala> implicit val a = "test" // define an implicit value of type String
a: java.lang.String = test
scala> val b = implicitly[String] // search for an implicit value of type String and assign it to b
b: String = test
scala> val c = implicitly[Int] // search for an implicit value of type Int and assign it to c
<console>:6: error: could not find implicit value for parameter e: Int
       val c = implicitly[Int]
                         ^

6
Метод точно не проверяет; Кажется, это вызывает ошибку компиляции, если неявное значение не доступно и, если оно есть, кажется, его извлекает. Можете ли вы предоставить больше контекста о том, почему я бы хотел использовать это?
davetron5000

17
implicitly[Ordering[(Int, String)]].compare( (1, "b"), (1, "a") )особенно для извлечения неявного параметра, введенного ограничением контекста:def foo[A: Ordering](a1: A, a2: A) = implicitly[Ordering[A]].compare(a1, a2)
retronym

1
Чтобы увидеть обсуждение ретронима в видео ссылке выше, перейдите к пункту 13:50.
chaotic3quilibrium

-2

Ответ «научить вас ловить рыбу» заключается в использовании алфавитного индекса, доступного в настоящее время в ночных еженедельниках Скаладока . Буквы (и #, для неалфавитных имен) в верхней части панели пакета / класса являются ссылками на индекс для имен членов, начинающихся с этой буквы (для всех классов). Если вы выберете I, например, вы найдете implicitlyзапись с одним вхождением, в Predefкоторой вы можете перейти по ссылке там.


46
Конечно, эти скалярные документы ничего не говорят о неявном, так что вряд ли это считается документацией. Как кто-то может понять, что этот метод делает из одних только этих документов? Я чувствую себя постоянно разочарованным в документации Scala. Поведение таких методов, как неявно, далеко не очевидно, и документация по ним едва ли лучше, чем не существует. Слава богу за переполнение стека. / end rant
Jeff


4
Тип подписи документирует это довольно хорошо.
ретроним

21
implicitкажется важной языковой особенностью в Scala и, безусловно, заслуживает правильного объяснения. Думать, что документы, в которых указывается только количество подписей типов, больше похоже на интеллектуальное самоудовлетворение, чем на подлинный ответ. Посмотрите конкретные вопросы, заданные ОП - что это такое и как оно используется? Ни на этот вопрос, ни в ночных документах, на которые вы даже не предоставили фактическую ссылку. scala-lang.org/files/archive/nightly/docs/library/… Это ничему не учит. См. Niklaus Wirth или Turbo Pascal для примеров подлинных документов. -1
Томас В.

3
implicitи implicitlyсвязаны, но довольно отчетливо. implicitКлючевым словом является частью языка. implicitlyопределяется в виде простого кода Scala в стандартной библиотеке. Поскольку он-лайн документы содержат ссылки на источники, я считаю, что все же лучше направлять вопросы к этим документам и связанному источнику.
Рэндалл Шульц
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.