¿Qué es el identificador Scala "implícitamente"?

169

He visto una función llamada implicitlyutilizada en los ejemplos de Scala. ¿Qué es y cómo se usa?

Ejemplo aquí :

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

Tenga en cuenta que tenemos que escribir implicitly[Foo[A]].apply(x)ya que el compilador piensa que eso implicitly[Foo[A]](x)significa que llamamos implicitlycon parámetros.

Consulte también Cómo investigar objetos / tipos / etc. de Scala REPL? y ¿Dónde busca Scala implicits?

oluies
fuente

Respuestas:

206

Aquí hay algunas razones para usar el método deliciosamente simple implicitly.

Para comprender / solucionar problemas de vistas implícitas

Una vista implícita se puede activar cuando el prefijo de una selección (considere, por ejemplo, the.prefix.selection(args)no contiene un miembro selectionque sea aplicable args(incluso después de intentar convertir argscon vistas implícitas). En este caso, el compilador busca miembros implícitos, definidos localmente en los ámbitos actuales o delimitadores, heredados o importados, que son funciones desde el tipo de ese the.prefixtipo hasta un tipo con selectionmétodos implícitos definidos o equivalentes.

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

Las Vistas implícitas también se pueden activar cuando una expresión no se ajusta al Tipo esperado, como se muestra a continuación:

scala> 1: scala.runtime.RichInt
res25: scala.runtime.RichInt = 1

Aquí el compilador busca esta función:

scala> implicitly[Int => scala.runtime.RichInt]
res26: (Int) => scala.runtime.RichInt = <function1>

Acceso a un parámetro implícito introducido por un contexto limitado

Los parámetros implícitos son posiblemente una característica más importante de Scala que las Vistas implícitas. Soportan el patrón de clase de tipo. La biblioteca estándar usa esto en algunos lugares: vea scala.Orderingy cómo se usa SeqLike#sorted. Los parámetros implícitos también se utilizan para pasar manifiestos de matriz e CanBuildFrominstancias.

Scala 2.8 permite una sintaxis abreviada para parámetros implícitos, llamados límites de contexto. Brevemente, un método con un parámetro de tipo Aque requiere un parámetro implícito de tipo M[A]:

def foo[A](implicit ma: M[A])

puede reescribirse como:

def foo[A: M]

Pero, ¿qué sentido tiene pasar el parámetro implícito pero no nombrarlo? ¿Cómo puede ser útil esto al implementar el método foo?

A menudo, no es necesario hacer referencia directa al parámetro implícito, se canalizará como un argumento implícito a otro método que se llama. Si es necesario, aún puede conservar la firma del método con el Context Bound y llamar implicitlypara materializar el valor:

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

Pasar un subconjunto de parámetros implícitos explícitamente

Supongamos que está llamando a un método que imprime bastante a una persona, utilizando un enfoque basado en clases de 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)

¿Qué sucede si queremos cambiar la forma en que se genera el nombre? Podemos llamar explícitamente PersonShow, pasar explícitamente una alternativa Show[String], pero queremos que el compilador pase la Show[Int].

Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)
retrónimo
fuente
2
scala> 1.min (2) res0: Int = 1 En Scala 2.10.3 obtengo un error: scala> implícitamente [Int => {def min (i: Int): Any}] <consola>: 8: error: No hay una vista implícita disponible de Int => AnyRef {def min (i: Int): Any}. implícitamente [Int => {def min (i: Int): Any}]
jhegedus
Esta respuesta se actualizará para la última versión.
es el
1
implícitamente [Int => AnyVal {def min (i: Int): Int}] funcionará. Debe ser arreglado en la respuesta.
Malkaviano
212

Implicitlyestá disponible en Scala 2.8 y se define en Predef como:

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

Se usa comúnmente para verificar si un valor implícito de tipo Testá disponible y devolverlo si ese es el caso.

Ejemplo simple de la presentación del retrónimo :

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]
                         ^
oluies
fuente
66
El método no verifica exactamente; parece causar un error de compilación si no hay un valor implícito disponible y, si lo hay, parece recuperarlo. ¿Puedes darnos un poco más de contexto sobre por qué querría usar esto?
davetron5000
17
implicitly[Ordering[(Int, String)]].compare( (1, "b"), (1, "a") ), especialmente para recuperar un parámetro implícito introducido por un Context Bound:def foo[A: Ordering](a1: A, a2: A) = implicitly[Ordering[A]].compare(a1, a2)
retronym
1
Para ver la discusión del retrónimo en el enlace de video anterior, salte al punto 13:50.
chaotic3quilibrium
-2

Una respuesta de "enseñarle a pescar" es usar el índice de miembros alfabéticos actualmente disponible en los nightlies de Scaladoc . Las letras (y #, para los nombres no alfabéticos) en la parte superior del panel del paquete / clase son enlaces al índice de los nombres de los miembros que comienzan con esa letra (en todas las clases). Si elige I, por ejemplo, encontrará la implicitlyentrada con una sola aparición, en Predef, que puede visitar desde el enlace allí.

Randall Schulz
fuente
46
Por supuesto, esos scaladocs no dicen nada implícitamente, por lo que eso apenas cuenta como documentación. ¿Cómo podría alguien averiguar qué hace ese método solo con esos documentos? Rutinariamente me siento decepcionado por la documentación de Scala. El comportamiento de métodos como implícitamente está lejos de ser obvio, y la documentación sobre ellos es apenas mejor que la inexistente. Gracias a Dios por Stack Overflow. / fin despotricar
Jeff
44
El tipo de firma documenta bastante bien este.
retronym
21
implicitparece ser una característica importante del lenguaje en Scala, y definitivamente merece una explicación adecuada. Pensar que los documentos que detallan solo un recuento de firmas de tipo parece más una autogratificación intelectual que una respuesta genuina. Vea las preguntas específicas formuladas por el OP: ¿qué es y cómo se usa? Ni respondido por esto, ni en los documentos nocturnos a los que ni siquiera proporciona un enlace real. scala-lang.org/files/archive/nightly/docs/library/… Esto no enseña nada. Consulte Niklaus Wirth o Turbo Pascal para ver ejemplos de documentos genuinos. -1
Thomas W
3
implicity implicitlyestán relacionados, pero son muy distintos. La implicitpalabra clave es parte del lenguaje. implicitlyse define en código Scala simple en la biblioteca estándar. Dado que los documentos en línea incluyen enlaces de origen, creo que aún es mejor remitir a los interrogadores a esos documentos y a la fuente vinculada.
Randall Schulz