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