118 votes

Qu'est-ce qu'un "context bound" en Scala ?

L'une des nouvelles fonctionnalités de Scala 2.8 sont les limites de contexte. Qu'est-ce qu'une limite de contexte et quelle est son utilité ?

Bien sûr, j'ai d'abord cherché (et trouvé par exemple ce ) mais je n'ai pas trouvé d'informations vraiment claires et détaillées.

155voto

Ben Lings Points 11395

La réponse de Robert couvre les détails techniques des limites du contexte. Je vais vous donner mon interprétation de leur signification.

En Scala, une vue liée ( A <% B ) capture le concept de "peut être vu comme" (alors qu'une limite supérieure <: capture le concept de "est un"). Une liaison contextuelle ( A : C ) dit "a" à propos d'un type. Vous pouvez lire les exemples sur les manifestes comme " T a un Manifest ". L'exemple que vous avez cité à propos de Ordered vs Ordering illustre la différence. Une méthode

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

dit que le paramètre peut être vu comme un Ordered . Comparer avec

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

qui dit que le paramètre a un associé Ordering .

En termes d'utilisation, je ne pense pas que les conventions soient bien établies (parce que les limites du contexte sont nouvelles). Une suggestion est que leur utilisation est privilégiée lorsque vous avez besoin de transférer une définition implicite d'une portée à une autre sans avoir besoin d'y faire référence directement (c'est certainement le cas pour la fonction ClassManifest utilisé pour créer un tableau).

Une autre façon de penser aux limites de vue et aux limites de contexte est que la première transfère les conversions implicites de la portée de l'appelant. La seconde transfère les objets implicites de la portée de l'appelant.

111voto

Robert Harvey Points 103562

Avez-vous trouvé cet article ? Il couvre la nouvelle fonction de liaison contextuelle, dans le cadre des améliorations apportées aux tableaux.

En général, un paramètre de type avec un lié au contexte est de la forme [T: Bound] ; il est étendu au paramètre de type simple T ainsi qu'un paramètre implicite de type Bound[T] .

Considérons la méthode tabulate qui forme un tableau à partir des résultats de l'application une fonction donnée f sur une plage de nombres allant de 0 à une longueur donnée. Jusqu'à Scala 2.7, tabulate pouvait être être écrit comme suit :

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

Dans Scala 2.8, ce n'est plus possible, car des informations d'exécution sont nécessaires pour créer la bonne représentation de Array[T] . On doit fournir cette information en passant un ClassManifest[T] dans la méthode en tant que paramètre implicite :

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
}

Sous une forme abrégée, une lié au contexte peut être utilisé sur le paramètre de type T au lieu de cela, donner :

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
}

39voto

retronym Points 35066

(Ceci est une parenthèse. Lisez et comprenez d'abord les autres réponses).

Les limites de contexte généralisent en fait les limites de vue.

Donc, étant donné ce code exprimé avec un 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

Ceci pourrait également être exprimé avec un Context Bound, à l'aide d'un alias de type représentant les fonctions de type F de taper 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 contexte lié doit être utilisé avec un constructeur de type de type * => * . Cependant, le constructeur de type Function1 est de type (*, *) => * . L'utilisation de l'alias de type applique partiellement le deuxième paramètre de type avec le type String en produisant un constructeur de type de type correct pour l'utilisation en tant que context bound.

Il existe une proposition visant à vous permettre d'exprimer directement des types partiellement appliqués en Scala, sans utiliser l'alias de type à l'intérieur d'un trait. Vous pourriez alors écrire :

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

19voto

Aaron Novstrup Points 10742

C'est une autre parenthèse.

Comme Ben a souligné Dans le cas d'un paramètre de type, un lien contextuel représente une contrainte "has-a" entre un paramètre de type et une classe de type. En d'autres termes, elle représente une contrainte selon laquelle une valeur implicite d'une classe de type particulière existe.

Lorsque l'on utilise une contrainte contextuelle, il est souvent nécessaire de faire apparaître cette valeur implicite. Par exemple, étant donné la contrainte T : Ordering on aura souvent besoin de l'instance de Ordering[T] qui satisfait à la contrainte. Comme démontré ici il est possible d'accéder à la valeur implicite en utilisant la fonction implicitly ou une méthode un peu plus utile context méthode :

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

ou

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X