Comment puis-je obtenir une instance de la classe de type associé à un contexte lié?

StackOverflow https://stackoverflow.com/questions/4373070

  •  09-10-2019
  •  | 
  •  

Question

Note:. Je pose cette question à lui répondre moi-même, mais d'autres réponses sont les bienvenus

Considérez la méthode simple suivante:

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

Je peux réécrire cela en utilisant un contexte lié ?? comme suit

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

mais comment puis-je obtenir une instance de type Numeric[T] afin que je puisse appeler la méthode plus?

Était-ce utile?

La solution

En utilisant la méthode implicitement

La plus courante et approche générale est d'utiliser la méthode implicitement , défini dans Predef:

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

De toute évidence, cela est un peu bavard et on doit répéter le nom de la classe de type.

Référencez le paramètre PREUVES ( ne pas )

Une autre alternative est d'utiliser le nom du paramètre de preuve implicite généré automatiquement par le compilateur:

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

Il est surprenant de constater que cette technique est encore légale, et il ne doit pas être invoqué dans la pratique puisque le nom du paramètre de preuve pourrait changer.

contexte d'un supérieur Kind ( introduire la méthode de context )

Au lieu de cela, on peut utiliser une version plus musclée de la méthode implicitly. Notez que la méthode implicite est définie comme

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

Cette méthode repose simplement sur le compilateur d'insérer un objet implicite du type correct du champ environnant dans l'appel de méthode, puis retourne. Nous pouvons faire un peu mieux:

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

Cela nous permet de définir notre méthode de add comme

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

Les paramètres de type méthode context Numeric et T sont déduites du champ d'application! Malheureusement, il y a des circonstances dans lesquelles cette méthode de context ne fonctionnera pas. Quand un paramètre de type a plusieurs limites de contexte ou il y a des paramètres multiples avec bornes de contexte différent, par exemple. Nous pouvons résoudre ce dernier problème avec une version un peu plus complexe:

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

Cette version nous oblige à préciser le type paramètre chaque fois, mais gère plusieurs paramètres de type.

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

Autres conseils

Au moins depuis Scala 2.9, vous pouvez faire ce qui suit:

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

Cette réponse décrit une autre approche que les résultats dans plus lisible, code client auto-documenté.

Motivation

méthode context que je l'ai décrit précédemment est une solution très générale qui fonctionne avec une classe de type, sans aucun effort supplémentaire. Cependant, il peut être indésirable pour deux raisons:

  • La méthode de context ne peut pas être utilisé lorsque le paramètre de type a de multiples bornes de contexte, étant donné que le compilateur n'a aucun moyen de déterminer quel contexte lié est destiné.

  • La référence à la lisibilité des méfaits de la méthode de context générique du code client.

méthodes spécifiques de type classe

En utilisant une méthode qui est liée à la classe de type désiré rend le code client beaucoup plus lisible. Telle est l'approche utilisée dans la bibliothèque standard pour la classe de type Manifest:

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

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

Généraliser cette approche

Le principal inconvénient de l'utilisation de méthodes d'spécifiques de type de classe est que une méthode supplémentaire doit être définie pour chaque classe de types. Nous pouvons faciliter ce processus avec les définitions suivantes:

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

Ensuite, une nouvelle méthode implicitement style spécifique de classe de type peut être défini, pour une classe de type:

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

Enfin, le code client peut utiliser le suit comme Implicitement:

def add[T: Numeric](x: T, y: T) = numeric[T].plus(x, y)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top