Pregunta

Nota:. Estoy plantear esta pregunta a responder por mí mismo, pero otras respuestas son bienvenidos

Considere el siguiente método sencillo:

def add[T](x: T, y: T)(implicit num: Numeric[T]) = num.plus(x,y)

I puede reescribir esta usando un contexto href="https://stackoverflow.com/questions/2982276/what-is-a-context-bound-in-scala"> como sigue

def add[T: Numeric](x: T, y: T) = ??.plus(x,y) 

Pero, ¿cómo puedo obtener una instancia del tipo Numeric[T] para que pueda invocar el método plus?

¿Fue útil?

Solución

Uso de la implícitamente Método

El más común y el enfoque general es utilizar la implícitamente método , definido en Predef:

def add[T: Numeric](x: T, y: T) = implicitly[Numeric[T]].plus(x,y)

Obviamente, esto es un poco prolijo y requiere repetir el nombre de la clase de tipo.

Hacer referencia a la evidencia parámetro ( No )

Otra alternativa es utilizar el nombre del parámetro evidencia implícita generada automáticamente por el compilador:

def add[T: Numeric](x: T, y: T) = evidence$1.plus(x,y)

Es sorprendente que esta técnica es incluso legal, y no se debe confiar en la práctica, ya que el nombre del parámetro evidencia podría cambiar.

Contexto de una clase superior ( introducir el método context )

En su lugar, se puede utilizar una versión reforzada por el método implicitly. Tenga en cuenta que el método implícitamente se define como

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

Este método simplemente se basa en el compilador para insertar un objeto implícito del tipo correcto del ámbito circundante en la llamada al método, y después lo devuelve. Podemos hacer un poco mejor:

def context[C[_], T](implicit e: C[T]) = e

Esto nos permite definir nuestro método add como

def add[T: Numeric](x: T, y: T) = context.plus(x,y)

El context parámetros de tipo método Numeric y T se infiere a partir del alcance! Por desgracia, hay circunstancias en las que este método context no funcionará. Cuando un parámetro de tipo tiene varios límites de contexto o hay múltiples parámetros con diferentes límites de contexto, por ejemplo. Podemos resolver este último problema con una versión ligeramente más complejo:

class Context[T] { def apply[C[_]]()(implicit e: C[T]) = e }
def context[T] = new Context[T]

Esta versión requiere que especifique el tipo de parámetro cada vez, pero maneja múltiples parámetros de tipo.

def add[T: Numeric](x: T, y: T) = context[T]().plus(x,y)

Otros consejos

Al menos desde Scala 2.9 puede hacer lo siguiente:

import Numeric.Implicits._
def add[T: Numeric](x: T, y: T) = x + y

add(2.8, 0.1) // res1: Double = 2.9
add(1, 2) // res2: Int = 3

Esta respuesta describe otro enfoque que da como resultado código de cliente más legible, autodocumentado.

La motivación

La context método que he descrito anteriormente es una solución muy general que funciona con cualquier tipo de clase, sin ningún esfuerzo adicional. Sin embargo, puede ser indeseable por dos razones:

  • El método context no puede ser utilizado cuando el parámetro de tipo tiene varios límites de contexto, ya que el compilador no tiene manera de determinar qué contexto ligado se pretende.

  • La referencia a la context genérica daños método legibilidad del código del cliente.

métodos Tipo de clase específicos

El uso de un método que está ligado a la clase tipo deseado del código de cliente hace mucho más fácil de leer. Este es el enfoque utilizado en la biblioteca estándar para la clase de tipo Manifiesto:

// definition in Predef
def manifest[T](implicit m: Manifest[T]) = m

// example usage
def getErasure[T: Manifest](x: T) = manifest[T].erasure

La generalización de este enfoque

El principal inconveniente de la utilización de métodos de clase específicos de tipo es que un método adicional debe estar definido para cada clase de tipo. Podemos facilitar este proceso con las siguientes definiciones:

class Implicitly[TC[_]] { def apply[T]()(implicit e: TC[T]) = e }
object Implicitly { def apply[TC[_]] = new Implicitly[TC] }

A continuación, un nuevo tipo de clase específica método implícitamente de estilo se puede definir, para cualquier clase de tipo:

def numeric = Implicitly[Numeric]
// or
val numeric = Implicitly[Numeric]

Finalmente, el código de cliente puede utilizar el Implícitamente de la siguiente manera:

def add[T: Numeric](x: T, y: T) = numeric[T].plus(x, y)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top