Cos’è “implicitamente” l’identificatore Scala?
Domanda
Ho visto una funzione denominata implicitly
utilizzato negli esempi di Scala.Cos'è e come viene utilizzato?
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))
^
Nota che dobbiamo scrivere implicitly[Foo[A]].apply(x)
poiché il compilatore lo pensa implicitly[Foo[A]](x)
significa che chiamiamo implicitly
con parametri.
Vedi anche Come indagare su oggetti/tipi/ecc.dalla Scala REPL? E Dove cerca Scala gli impliciti?
Soluzione
Ecco alcuni motivi per utilizzare questo metodo deliziosamente semplice implicitly
.
Comprendere/risolvere i problemi relativi alle visualizzazioni implicite
Una vista implicita può essere attivata quando il prefisso di una selezione (considerare ad esempio, the.prefix.selection(args)
non contiene un membro selection
questo è applicabile a args
(anche dopo aver provato a convertire args
con visualizzazioni implicite).In questo caso, il compilatore cerca membri impliciti, definiti localmente negli ambiti corrente o di inclusione, ereditati o importati, che siano funzioni del tipo di quello the.prefix
a un tipo con selection
metodi impliciti definiti o equivalenti.
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
Le visualizzazioni implicite possono anche essere attivate quando un'espressione non è conforme al tipo previsto, come di seguito:
scala> 1: scala.runtime.RichInt
res25: scala.runtime.RichInt = 1
Qui il compilatore cerca questa funzione:
scala> implicitly[Int => scala.runtime.RichInt]
res26: (Int) => scala.runtime.RichInt = <function1>
Accesso a un parametro implicito introdotto da un contesto vincolato
I parametri impliciti sono probabilmente una caratteristica più importante di Scala rispetto alle visualizzazioni implicite.Supportano il modello di classe tipo.La libreria standard lo usa in alcuni posti - vedi scala.Ordering
e come viene utilizzato SeqLike#sorted
.I parametri impliciti vengono utilizzati anche per passare manifest di array e CanBuildFrom
istanze.
Scala 2.8 consente una sintassi abbreviata per i parametri impliciti, chiamata Limiti di contesto.In breve, un metodo con un parametro di tipo A
che richiede un parametro implicito di tipo M[A]
:
def foo[A](implicit ma: M[A])
può essere riscritto come:
def foo[A: M]
Ma che senso ha passare il parametro implicito ma non nominarlo?Come può essere utile quando si implementa il metodo foo
?
Spesso non è necessario fare riferimento direttamente al parametro implicito, ma verrà incanalato come argomento implicito in un altro metodo chiamato.Se è necessario, puoi comunque conservare la firma concisa del metodo con Context Bound e chiamare implicitly
per materializzare il valore:
def foo[A: M] = {
val ma = implicitly[M[A]]
}
Passaggio esplicito di un sottoinsieme di parametri impliciti
Supponiamo di chiamare un metodo che stampa in modo carino una persona, utilizzando un approccio basato sulla classe di tipo:
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)
E se volessimo cambiare il modo in cui viene visualizzato il nome?Possiamo chiamare esplicitamente PersonShow
, passa esplicitamente un'alternativa Show[String]
, ma vogliamo che il compilatore passi il file Show[Int]
.
Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)
Altri suggerimenti
Implicitly
è Disponibile in Scala 2.8 ed è definito nella Predef come:
def implicitly[T](implicit e: T): T = e
È comunemente usato per controllo se un implicito valore di tipo T
è disponibile e restituirlo se tale è il caso.
Esempio semplice da di retronym presentazione :
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]
^
A "insegnerà a pescare" risposta è quella di utilizzare l'indice membro alfabetica attualmente disponibile nel Scaladoc . Le lettere (e il #
, per i nomi non alfabetici) nella parte superiore del riquadro pacchetto / classe sono collegamenti con l'indice per i nomi dei membri che iniziano con quella lettera (in tutte le classi). Se si sceglie I
, per esempio, troverete la voce implicitly
con una sola occorrenza, in Predef
, che si può visitare dal link lì.