Il y a principalement deux erreurs dans votre code:
- Lors de l'extension
Expr
vous oublié de passer le paramètre de type
- Dans la branche
Sum
de votre modèle vous correspondant essayez de résumer deux T
s, sans donner suffisamment de preuves au compilateur du fait que l'opérateur +
est défini sur le type T
.
est ici une solution révisée qui fonctionne:
object expr {
abstract class Expr[T](implicit evidence: Numeric[T]) {
def eval: T = this match {
case Number(x) => x
case Sum(e1, e2) => evidence.plus(e1.eval, e2.eval)
}
def show: String = this match {
case Number(x) => "" + x
case Sum(e1, e2) => "(" + e1.show + "+" + e2.show + ")"
}
}
case class Number[T : Numeric](val value: T) extends Expr[T]
case class Sum[T : Numeric](val e1: Expr[T], val e2: Expr[T]) extends Expr[T]
}
Voici un exemple exécutée dans le shell Scala:
scala> import expr._
import expr._
scala> Sum(Sum(Number(2), Number(3)), Number(4))
expression: expr.Sum[Int] = Sum(Sum(Number(2),Number(3)),Number(4))
scala> println(expression.show + " = " + expression.eval)
((2+3)+4) = 9
Je suis sûr que le paramètre de type manquant pour le constructeur de type Expr
est juste une distraction et il n'a pas besoin d'explications supplémentaires.
Le code de mon exemple introduit un paramètre implicite evidence
et l'utilisation de la classe de type Numeric
. Dans Scala, une classe de type est un modèle qui utilise des traits et des paramètres implicites pour définir des capacités pour les classes avec un certain degré de flexibilité.
Afin de sommer deux valeurs génériques, le compilateur doit avoir un moyen de savoir que les deux T
s savent comment être sommés. Cependant, les types numériques ne sont pas dans une hiérarchie de type qui peut être exploitée en disant que T
est un sous-type d'une classe hypothétique Number
(en écrivant quelque chose comme abstract class Expr[T <: Number]
).
La bibliothèque standard Scala a cependant introduit la classe de type Numeric
, qui est fondamentalement un trait qui définit un ensemble d'opérations qui ont un sens pour tous les types numériques (d'où le nom). La méthode plus
est une gauche non implémentée pour quiconque veut adhérer à ce trait.
La bibliothèque standard Scala implémente ces caractéristiques pour différents types numériques, vous permettant ainsi d'utiliser sans effort cette même implémentation générique avec différents types.
Voici un exemple:
scala> val expression = Sum(Sum(Number(2.0), Number(3.0)), Number(4.0))
expression: expr.Sum[Double] = Sum(Sum(Number(2.0),Number(3.0)),Number(4.0))
scala> println(expression.show + " = " + expression.eval)
((2.0+3.0)+4.0) = 9.0
Spécification d'une preuve implicite comme cela peut être fait explicitement (comme dans abstract class Expr[T](implicit evidence: Numeric[T])
) ou en utilisant la soi-disant notation « contexte lié » (comme dans case class Number[T : Numeric]
), qui est sucre essentiellement syntaxique pour la variante explicite qui renonce à deux référencer explicitement une instance de classe de type. J'ai utilisé la variante explicite dans le premier cas parce que je devais référencer l'instance de classe de type dans mon code pour additionner les deux valeurs (evidence.plus(e1.eval, e2.eval)
) mais j'ai utilisé la notation «contexte lié» dans ce dernier cas car je la trouve plus naturelle lisible.
Si vous le souhaitez, vous pouvez également mettre en œuvre Numeric[T]
pour vos propres classes (par exemple .: Numeric[Rational]
) sans avoir à faire face à une hiérarchie de type statique.
Ceci est bien sûr une explication très précipitée des classes de types: pour une explication plus approfondie, vous proposez ce très bon blog post sur le sujet.
Si la réponse fournie est valide, il serait bon de la marquer comme acceptée. :-) – stefanobaghino