Что такое идентификатор Scala «неявно»? - PullRequest
158 голосов
/ 04 октября 2010

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

Ответы [ 3 ]

195 голосов
/ 05 октября 2010

Вот несколько причин использовать восхитительно простой метод 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)
193 голосов
/ 04 октября 2010

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]
                         ^
1 голос
/ 04 октября 2010

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

...