scala scala-2.8 context-bound

¿Qué es un "contexto vinculado" en Scala?



scala-2.8 context-bound (4)

¿Encontraste este artículo ? Cubre la nueva característica vinculada al contexto, en el contexto de las mejoras de la matriz.

Generalmente, un parámetro de tipo con un límite de contexto es de la forma [T: Bound] ; se expande al parámetro de tipo simple T junto con un parámetro implícito de tipo Bound[T] .

Considere el método de tabulate que forma una matriz a partir de los resultados de aplicar una función dada f en un rango de números desde 0 hasta una longitud determinada. Hasta Scala 2.7, la tabulación 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] . Es necesario 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 una forma abreviada, un límite de contexto se puede utilizar en el parámetro de tipo T lugar, 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 }

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

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


La respuesta de Robert cubre los detalles técnicos de Context Bounds. Te daré mi interpretación de su significado.

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

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

dice que el parámetro se puede ver como Ordered . Comparar con

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

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

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

Otra forma de pensar sobre los límites de vista y los límites de contexto es que el primero transfiere las conversiones implícitas desde el 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 comprenda las otras respuestas primero).

Los límites de contexto realmente generalizan View Bounds.

Entonces, dado este código expresado con View Bound:

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 se puede expresar con un enlace de contexto, con la ayuda de un alias de tipo que representa funciones del tipo F al tipo 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 enlazado se debe usar con un constructor de tipos de tipo * => * . Sin embargo, el constructor de tipos Function1 es de tipo (*, *) => * . El uso del alias de tipo aplica parcialmente el segundo parámetro de tipo con el tipo String , produciendo un constructor de tipo del tipo correcto para usar como un contexto vinculado.

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

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


Esta es otra nota parentética.

Como señaló Ben , un límite de contexto representa una restricción "tiene-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 particular.

Al utilizar un límite de contexto, a menudo es necesario que surja ese valor implícito. Por ejemplo, dada la restricción T : Ordering , a menudo se necesitará la instancia de Ordering[T] que satisfaga la restricción. Como se demuestra aquí , es posible acceder al valor implícito utilizando el método implicitly o un método de context ligeramente más útil:

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