Що таке ідентифікатор 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допомогою Implicit Views). У цьому випадку компілятор шукає неявних членів, локально визначених у поточних або додаючих областях, успадкованих або імпортованих, що є або функціями від типу цього типу 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, ніж Implicit Views. Вони підтримують модель класу типу. Стандартна бібліотека використовує це в кількох місцях - дивіться, 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): Будь-який}] <console>: 8: error: Немає неявного перегляду з Int => AnyRef {def min (i: Int): Any}. неявно [Int => {def min (i: Int): Будь-який}]
jhegedus

Ця відповідь буде оновлена ​​для останньої версії.
emeth

1
неявно [Int => AnyVal {def min (i: Int): Int}] буде працювати. Слід зафіксувати у відповіді.
Malkaviano

212

Implicitlyдоступний в Scala 2.8 і визначається в Preef як:

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)
ретронім

1
Щоб побачити обговорення ретроніму у відеопосиланні вище, перейдіть до пункту 13:50.
хаотичний3рівноваги

-2

Відповідь "навчити ловити рибу" полягає у використанні буквеного індексу членів, який зараз доступний у солов'їнах Scaladoc . Букви (і #для не алфавітних імен) у верхній частині панелі пакунків / класів є посиланнями на індекс для імен членів, що починаються з цієї літери (для всіх класів). Якщо ви виберете 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
Thomas W

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