Pregunta

Una de las nuevas características de Scala 2.8 son los límites de contexto. ¿Qué es un contexto atado y dónde es útil?

Por supuesto que busqué primero (y encontré por ejemplo este) Pero no pude encontrar información realmente clara y detallada.

¿Fue útil?

Solución

Encontraste Este artículo? Cubre la nueva característica de contexto, dentro del contexto de las mejoras de matriz.

En general, un parámetro de tipo con un Contexto es de la forma [T: Bound]; se expande al parámetro de tipo simple T junto con un parámetro de tipo implícito Bound[T].

Considere el método tabulate que forma una matriz a partir de los resultados de aplicar una función f dada en un rango de números desde 0 hasta una longitud dada. Hasta Scala 2.7, Tabule podría escribirse de la siguiente manera:

def tabulate[T](len: Int, f: Int => T) = {
    val xs = new Array[T](len)
    for (i <- 0 until len) xs(i) = f(i)
    xs
}

En Scala 2.8 esto ya no es posible, porque la información de tiempo de ejecución es necesaria para crear la representación correcta de Array[T]. Uno necesita proporcionar esta información pasando un ClassManifest[T] en el método como un parámetro implícito:

def tabulate[T](len: Int, f: Int => T)(implicit m: ClassManifest[T]) = {
    val xs = new Array[T](len)
    for (i <- 0 until len) xs(i) = f(i)
    xs
}

Como forma abreviada, un Contexto se puede usar en el parámetro de tipo T En cambio, dando:

def tabulate[T: ClassManifest](len: Int, f: Int => T) = {
    val xs = new Array[T](len)
    for (i <- 0 until len) xs(i) = f(i)
    xs
}

Otros consejos

La respuesta de Robert cubre los detalles técnicos de los límites de contexto. Te daré mi interpretación de su significado.

En Scala A View Bound (A <% B) captura el concepto de 'se puede ver como' (mientras que un límite superior <: captura el concepto de 'es un'). Un contexto atado (A : C) dice 'tiene un' sobre un tipo. Puedes leer los ejemplos sobre manifiestos como "T tiene un Manifest". El ejemplo al que se vinculó sobre Ordered VS Ordering ilustra la diferencia. Un método

def example[T <% Ordered[T]](param: T)

dice que el parámetro puede verse como un Ordered. Comparar con

def example[T : Ordering](param: T)

que dice que el parámetro tiene un asociado Ordering.

En términos de uso, las convenciones tardaron un tiempo en establecerse, pero los límites de contexto se prefieren sobre los límites de la vista (Los límites de la vista ahora están en desuso). Una sugerencia es que se prefiere un contexto límite cuando necesita transferir una definición implícita de un alcance a otro sin necesidad de referirse a él directamente (este es ciertamente el caso del caso para el ClassManifest utilizado para crear una matriz).

Otra forma de pensar sobre los límites de la vista y los límites de contexto es que la primera transfiere conversiones implícitas del alcance de la persona que llama. El segundo transfiere objetos implícitos del alcance de la persona que llama.

(Esta es una nota entre paréntesis. Lea y comprende las otras respuestas primero).

Los límites de contexto en realidad generalizan los límites de la vista.

Entonces, dado este código expresado con una visión vinculada:

scala> implicit def int2str(i: Int): String = i.toString
int2str: (i: Int)String

scala> def f1[T <% String](t: T) = 0
f1: [T](t: T)(implicit evidence$1: (T) => String)Int

Esto también podría expresarse con un contexto limitado, con la ayuda de un tipo de alias que representan funciones de tipo F digitar T.

scala> trait To[T] { type From[F] = F => T }           
defined trait To

scala> def f2[T : To[String]#From](t: T) = 0       
f2: [T](t: T)(implicit evidence$1: (T) => java.lang.String)Int

scala> f2(1)
res1: Int = 0

Un contexto de contexto debe usarse con un constructor de tipos de especie * => *. Sin embargo, el constructor de tipos Function1 es de amable (*, *) => *. El uso del tipo de alias se aplica parcialmente el parámetro de segundo tipo con el tipo String, produciendo un constructor de tipo del tipo correcto para su uso como un contexto límite.

Existe una propuesta para permitirle expresar directamente tipos parcialmente aplicados en Scala, sin el uso del tipo de alias dentro de un rasgo. Entonces podrías escribir:

def f3[T : [X](X => String)](t: T) = 0 

Esta es otra nota entre paréntesis.

Como Ben señaló, un límite de contexto representa una restricción "HA-A" entre un parámetro de tipo y una clase de tipo. Dicho de otra manera, representa una restricción de que existe un valor implícito de una clase de tipo en particular.

Al utilizar un contexto limitado, a menudo uno necesita surgir ese valor implícito. Por ejemplo, dada la restricción T : Ordering, uno a menudo necesitará la instancia de Ordering[T] que satisface la restricción. Como se demostró aquí, es posible acceder al valor implícito utilizando el implicitly método o un poco más útil context método:

def **[T : Numeric](xs: Iterable[T], ys: Iterable[T]) = 
   xs zip ys map { t => implicitly[Numeric[T]].times(t._1, t._2) }

o

def **[T : Numeric](xs: Iterable[T], ys: Iterable[T]) =
   xs zip ys map { t => context[T]().times(t._1, t._2) }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top