所以这是代码:
package week4
object expr {
abstract class Expr[T] {
def eval:T = this match {
case Number(x) => x
case Sum(e1, e2) => 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](val value: T) extends Expr {
}
case class Sum[T](val e1: Expr[T], val e2: Expr[T]) extends Expr {
}
}
除了我得到所有案例比较的错误:
构造函数无法实例化为期望的类型;发现:week4.expr.Number [T(在类号中)]必需:week4.expr.Expr [T(in class Expr)]注意:没有<:T(和week4.expr.Number [T]<: week4.expr.Expr [Nothing]),但是类T的类Expr是不变的。你 可能希望将T定义为+ T.
我做错了什么?
答案 0 :(得分:11)
您的代码中主要有两个错误:
Expr
时,您忘记传递类型参数Sum
分支中,您试图将两个T
求和,而没有向编译器提供足够的证据证明+
运算符已在输入T
。这是一个有效的修订解决方案:
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]
}
以下是Scala shell中运行的示例:
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
我确定类型构造函数Expr
缺少的类型参数只是分散注意力,并且不需要进一步解释。
我的示例中的代码引入了隐式参数evidence
以及Numeric
类型类的用法。在Scala中,类型类是一种模式,它利用特征和隐式参数来定义具有一定灵活性的类的功能。
为了对两个通用值求和,编译器必须知道两个T
知道如何求和。但是,数字类型不在类型层次结构中,可以通过说T
是假设类Number
的子类型(通过编写类似abstract class Expr[T <: Number]
的内容)来利用它。
然而,Scala标准库引入了Numeric
类型类,它基本上是一个特性,它定义了一组对所有数字类型都有意义的操作(因此名称)。对于想要遵守这一特征的人,plus
方法是未实现的。
Scala标准库为各种数字类型实现了这种特性,因此您可以毫不费力地使用各种类型的相同通用实现。
以下是一个例子:
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
指定这样的隐式证据可以明确地完成(如在abstract class Expr[T](implicit evidence: Numeric[T])
中)或使用所谓的&#34;上下文绑定&#34;符号(如case class Number[T : Numeric]
中所示),它基本上是显式变体的语法糖,它放弃了显式引用类型类实例。我在第一种情况下使用了显式变体,因为我需要在代码中引用类型类实例来实际求和这两个值(evidence.plus(e1.eval, e2.eval)
),但我使用了&#34;上下文绑定&#34;在后一种情况下的符号,因为我觉得它更自然和可读。
如果您愿意,还可以为自己的类实现Numeric[T]
(例如:Numeric[Rational]
),而无需处理静态类型层次结构。
这当然是对类型类的非常仓促的解释:为了更详尽的解释,我建议你对这个主题非常好blog post。