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


He visto una función llamada implicitly usada en 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 implicitly[Foo[A]](x) significa que llamamos implicitly con parámetros.

También vea Cómo investigar objetos/tipos/etc. de Scala REPL? y ¿Dónde busca Scala implicitos?

Author: Community, 2010-10-04

3 answers

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

Para entender/solucionar problemas de Vistas implícitas

Una Vista implícita puede activarse cuando el prefijo de una selección (por ejemplo, the.prefix.selection(args) no contiene un miembro selection que sea aplicable a args (incluso después de intentar convertir args con Vistas Implícitas). En este caso, el compilador busca miembros implícitos, definidos localmente en los ámbitos actual o adjunto, heredados o importados, que son o bien funciones del tipo de ese the.prefix a un tipo con selection definido, o métodos implícitos 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>

Accediendo a un Parámetro Implícito Introducido por un Contexto Enlazado

Los parámetros implícitos son posiblemente una característica más importante de Scala que las Vistas implícitas. Ellos apoyan la tipo patrón de clase. La biblioteca estándar usa esto en algunos lugares see ver scala.Ordering y cómo se usa en SeqLike#sorted. Los parámetros implícitos también se utilizan para pasar manifiestos de matriz, e instancias CanBuildFrom.

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

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

Se puede reescribir como:

def foo[A: M]

Pero cuál es el punto de pasar el implícito parámetro pero no nombrarlo? ¿Cómo puede esto ser útil al implementar el método foo?

A menudo, el parámetro implícito no necesita ser referido directamente, será tunelizado como un argumento implícito a otro método que es llamado. Si es necesario, aún puede conservar la firma del método conciso con el Contexto enlazado, y llamar a implicitly para 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 bonito imprime 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é pasa si queremos cambiar la forma en que se genera el nombre? Podemos llamar explícitamente a PersonShow, pasar explícitamente una alternativa Show[String], pero queremos que el compilador pase el Show[Int].

Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)
 182
Author: retronym,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-23 11:39:16

Implicitly está disponible en Scala 2.8 y se define en Predef como:

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

Se usa comúnmente para comprobar si un valor implícito de tipo T está 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]
                         ^
 178
Author: oluies,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-18 21:11:57

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

 4
Author: Randall Schulz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-04 13:50:19