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

144

Я видел функцию с именем 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 искать неявки?

Теги:
implicits

3 ответа

178
Лучший ответ

Вот несколько причин использовать восхитительно простой метод 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 позволяет использовать сокращенный синтаксис для неявных параметров, называемый Context Bounds. Вкратце, метод с параметром типа A, который требует неявного параметра типа M[A]:

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

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

def foo[A: M]

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

Часто неявный параметр не нужно ссылаться напрямую, он будет туннелироваться как неявный аргумент для другого вызываемого метода. Если это необходимо, вы все равно можете сохранить красную подпись метода с помощью Context Bound и вызвать 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
    Вы должны добавить этот ответ здесь stackoverflow.com/q/1025181/203968
  • 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}]
Показать ещё 2 комментария
148

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
    Метод точно не проверяет; кажется, что это вызывает ошибку компиляции, если нет неявного доступного значения и, если оно есть, кажется, его извлекает. Можете ли вы предоставить больше контекста о том, почему я бы хотел использовать это?
  • 16
    implicitly[Ordering[(Int, String)]].compare( (1, "b"), (1, "a") ) , особенно для извлечения неявного параметра, введенного в контексте Bound: def foo[A: Ordering](a1: A, a2: A) = implicitly[Ordering[A]].compare(a1, a2)
Показать ещё 4 комментария
5

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

  • 42
    Конечно, эти скалярные документы ничего не говорят о неявном виде, так что вряд ли это считается документацией. Как кто-то может понять, что этот метод делает из одних только этих документов? Я чувствую себя постоянно разочарованным в документации Scala. Поведение таких методов, как неявно, далеко не очевидно, и документация по ним едва ли лучше, чем не существует. Слава богу за переполнение стека. / конец разглагольствования
  • 1
    @Джефф, см. Scala-programming-language.1934581.n4.nabble.com/…
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню