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

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

Ответы

Ответ 1

Вот несколько причин использовать восхитительно простой метод 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

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]
                         ^

Ответ 3

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