Qual è l'identificatore Scala "implicitamente"?


169

Ho visto una funzione chiamata implicitlyusata negli esempi di Scala. Che cos'è e come viene utilizzato?

Esempio qui :

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 pensa che ciò implicitly[Foo[A]](x)significhi che chiamiamo implicitlycon parametri.

Vedi anche Come investigare oggetti / tipi / ecc. da Scala REPL? e dove Scala cerca gli impliciti?

Risposte:


206

Ecco alcuni motivi per utilizzare il metodo deliziosamente semplice implicitly.

Per comprendere / risolvere i problemi relativi alle visualizzazioni implicite

Una vista implicita può essere attivata quando il prefisso di una selezione (ad esempio, the.prefix.selection(args)non contiene un membro selectionapplicabile args(anche dopo aver provato a convertire argscon viste implicite). In questo caso, il compilatore cerca membri impliciti, definiti localmente negli ambiti correnti o racchiusi, ereditati o importati, che sono funzioni dal tipo di quello the.prefixa un tipo con selectionmetodi 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 viste 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 associato

I parametri impliciti sono probabilmente una caratteristica più importante di Scala rispetto alle viste implicite. Supportano il modello di classe di tipo. La libreria standard lo utilizza in alcuni punti: vedere scala.Orderinge come viene utilizzata SeqLike#sorted. I parametri impliciti vengono anche utilizzati per passare manifesti e CanBuildFromistanze di array .

Scala 2.8 consente una sintassi abbreviata per i parametri impliciti, chiamati Context Bounds. In breve, un metodo con un parametro di tipo Ache 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à tunnelato come argomento implicito verso un altro metodo chiamato. Se è necessario, puoi comunque conservare la firma del metodo terse con il Context Bound e chiamare implicitlyper materializzare il valore:

def foo[A: M] = {
   val ma = implicitly[M[A]]
}

Passaggio esplicito di un sottoinsieme di parametri impliciti

Supponiamo che tu stia chiamando un metodo che stampa piuttosto una persona, usando 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 vogliamo cambiare il modo in cui il nome viene emesso? Possiamo chiamare esplicitamente PersonShow, passare esplicitamente un'alternativa Show[String], ma vogliamo che il compilatore passi il Show[Int].

Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)

2
scala> 1.min (2) res0: Int = 1 In Scala 2.10.3 Ricevo un errore: scala> implicitamente [Int => {def min (i: Int): Any}] <console>: 8: errore: Nessuna vista implicita disponibile da Int => AnyRef {def min (i: Int): Any}. implicitamente [Int => {def min (i: Int): Any}]
jhegedus

Questa risposta verrebbe aggiornata per l'ultima versione.
emeth

1
implicitamente [Int => AnyVal {def min (i: Int): Int}] funzionerà. Dovrebbe essere risolto nella risposta.
Malkaviano,

212

Implicitlyè disponibile in Scala 2.8 ed è definito in Predef come:

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

Viene comunemente utilizzato per verificare se è disponibile un valore implicito di tipo Te restituirlo in tal caso.

Semplice esempio dalla presentazione di 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]
                         ^

6
Il metodo non controlla esattamente; sembra causare un errore di compilazione se non è disponibile un valore implicito e, se esiste, sembra recuperarlo. Puoi fornire qualche altro contesto sul motivo per cui vorrei mai usarlo?
davetron5000,

17
implicitly[Ordering[(Int, String)]].compare( (1, "b"), (1, "a") ), in particolare per recuperare un parametro implicito introdotto da un Context Bound:def foo[A: Ordering](a1: A, a2: A) = implicitly[Ordering[A]].compare(a1, a2)
retronym

1
Per vedere la discussione di Retronym nel link del video sopra, vai al punto 13:50.
chaotic3quilibrium

-2

Una risposta "ti insegnano a pescare" è quella di utilizzare l'indice alfabetico dei membri attualmente disponibile nei nightly di Scaladoc . Le lettere (e il #, per i nomi non alfabetici) nella parte superiore del riquadro del pacchetto / classe sono collegamenti all'indice per i nomi dei membri che iniziano con quella lettera (in tutte le classi). Se scegli I, ad esempio, troverai la implicitlyvoce con un'occorrenza, in Predef, che puoi visitare dal link lì.


46
Naturalmente, quegli scaladoc non dicono nulla in modo implicito, quindi non conta quasi come documentazione. Come farebbe qualcuno a capire cosa fa quel metodo solo da quei documenti? Mi sento regolarmente deluso dalla documentazione di Scala. Il comportamento di metodi come implicitamente è tutt'altro che ovvio, e la documentazione su di essi è appena migliore di inesistente. Grazie a Dio per Stack Overflow. / end rant
Jeff


4
La firma del tipo lo documenta abbastanza bene.
retronym

21
implicitsembra essere un elemento linguistico importante in Scala e sicuramente degno di una spiegazione adeguata. Pensare che i documenti che descrivono solo un conteggio delle firme di tipo sembra più un'autogratificazione intellettuale, che una risposta autentica. Vedi le domande specifiche poste dall'OP - che cos'è e come viene utilizzato? Né risposto da questo, né nei documenti notturni a cui non fornisci nemmeno un link effettivo. scala-lang.org/files/archive/nightly/docs/library/… Questo non insegna nulla. Vedi Niklaus Wirth o Turbo Pascal per esempi di documenti originali. -1
Thomas W,

3
implicite implicitlysono correlati, ma abbastanza distinti. La implicitparola chiave fa parte della lingua. implicitlyè definito in semplice codice Scala nella libreria standard. Dal momento che i documenti online includono collegamenti di origine, credo che sia ancora meglio riferire gli interrogatori a quei documenti e alla fonte collegata.
Randall Schulz,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.