Что такое идентификатор 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 ищет последствия?
4 ответа
Вот несколько причин использовать восхитительно простой метод 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
?
Часто на неявный параметр не нужно ссылаться напрямую, он будет туннелироваться как неявный аргумент для другого вызываемого метода. Если это необходимо, вы все равно можете сохранить краткую подпись метода с привязкой к контексту и вызвать 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
доступно и верните его, если это так.
Простой пример из презентации ретронима:
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]
^
Starting Scala 3 has been replaced with improved which has the advantage of being able to return a more precise type than asked for
The method corresponds to implicitly in Scala 2. It isprecisely the same as the the method in Shapeless. The difference between (or the) and is that summon can return a more precise type than the type that was asked for.
For example given the following type
trait F[In]:
type Out
def f(v: Int): Out
given F[Int] with
type Out = String
def f(v: Int): String = v.toString
implicitly
method would summon a term with erased type member
scala> implicitly[F[Int]]
val res5: F[Int] = given_F_Int$@7d0e5fbb
scala> implicitly[res5.Out =:= String]
1 |implicitly[res5.Out =:= String]
| ^
| Cannot prove that res5.Out =:= String.
scala> val x: res5.Out = ""
1 |val x: res5.Out = ""
| ^^
| Found: ("" : String)
| Required: res5.Out
In order to recover the type member we would have to refer to it explicitly which defeats the purpose of having the type member instead of type parameter
scala> implicitly[F[Int] { type Out = String }]
val res6: F[Int]{Out = String} = given_F_Int$@7d0e5fbb
scala> implicitly[res6.Out =:= String]
val res7: res6.Out =:= String = generalized constraint
However summon
defined as
def summon[T](using inline x: T): x.type = x
does not suffer from this problem
scala> summon[F[Int]]
val res8: given_F_Int.type = given_F_Int$@7d0e5fbb
scala> summon[res8.Out =:= String]
val res9: String =:= String = generalized constraint
scala> val x: res8.Out = ""
val x: res8.Out = ""
where we see type member
type Out = String
did not get erased even though we only asked for
F[Int]
and not
F[Int] { type Out = String }
. This can prove particularly relevant when chaining dependently typed functions:
The type summoned by implicitly has no
Out
type member. For thisreason, we should avoid implicitly when working with dependently typedfunctions.
Ответ "научить вас ловить рыбу" заключается в использовании алфавитного индекса, доступного в настоящее время в ночных еженедельниках Скаладока. Буквы (и #
(для неалфавитных имен) в верхней части панели пакета / класса находятся ссылки на индекс для имен членов, начинающихся с этой буквы (для всех классов). Если вы выбираете I
Например, вы найдете implicitly
запись с одним вхождением, в Predef
, который вы можете посетить по ссылке там.