Что такое идентификатор 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: 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 с параметрами.

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

3 144

3 ответа:

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

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

неявное представление может быть вызвано, когда префикс выбора (рассмотрим, например,the.prefix.selection(args) не содержит это применимо к args (даже после попытки преобразования args С неявными представлениями). В этом случае компилятор ищет неявные члены, локально определенные в текущей или охватывающей областях, унаследованные или импортированные, которые являются либо функциями от типа that 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>

доступ к неявному параметру, введенному контекстной привязкой

неявные параметры, возможно, a более важная особенность 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)

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

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

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

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

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

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