拥有Java类:
class abstract Exp
class ExpAdd extends Exp
class ExpSub extends Exp
和Scala方法:
def interpret(e: ExpAdd) = interpret(e.left_arg) + interpret(e.right_arg)
def interpret(e: ExpSub) = interpret(e.left_arg) - interpret(e.right_arg)
def interpret(exp: Exp) = exp match {
case e : ExpAdd = interpret(e)
case e : ExpSub = interpret(e)
}
如何重构代码使更轻松地添加新的Exps ?我认为解释(Exp)根本不会被使用,因为另外两个更具体和
然而,在Scala中似乎并非如此。我也尝试删除解释(Exp),但编译器并不喜欢它。这就是我使用case子句的原因......有没有办法让重载按预期工作?或者可能是一些解决方法?如果多个成员方法都可访问且适用于a 方法调用,有必要选择一个提供 运行时方法调度的描述符。 Java编程 language使用选择最具体方法的规则。
答案 0 :(得分:2)
正如@Alexy所说,方法解析基于编译时或"静态"参数的类型。您可以将操作数的类型作为表达式类型的一部分,然后使用implicits以递归方式构造给定类型的解释器:
trait Exp
case class ExpAdd[A <: Exp, B <: Exp](left_arg: A, right_arg: B) extends Exp
case class ExpSub[A <: Exp, B <: Exp](left_arg: A, right_arg: B) extends Exp
case class Literal(i: Int) extends Exp
trait ExpInterpreter[E <: Exp] {
def interpret(e: E): Int
}
object ExpInterpreter {
implicit object LiteralInterpreter extends Interpreter[Literal] {
def interpret(l: Literal) = l.i
}
implicit def addInterpreter[A <: Exp, B <: Exp](
implicit leftInterpreter: Interpreter[A], rightInterpreter: Interpreter[B]) =
new Interpreter[ExpAdd[A, B]] {
def interpret(e: ExpAdd[A, B]) = leftInterpreter.interpret(e.left_arg) +
rightInterpreter.interpret(e.right_arg)
}
implicit def subInterpreter ...
}
然后,当您添加新的Exp
个案例时,您只需要定义一个新的隐式,并且您可以在任何地方执行此操作,而不必将案例添加到特定的匹配表达式。您甚至可以在库中使用Exp
和ExpInterpreter
,在不同的jar中使用案例和特定的解释器。
如上所述,这意味着表达式的结构是其类型的一部分(并且你必须携带类型,直到你解释它 - 没有办法解释泛型Exp
,只有E <: Exp: ExpInterpreter
),但也许可以使用与Free monad相似的技术来克服这个问题?
答案 1 :(得分:1)
您对Exp
和interpret
的定义并不完全可见,也不确定我理解您要表达的内容,但这里有2个想法:
+
和-
方法以进行更多DSL-esque操作答案 2 :(得分:1)
如果多个成员方法都可访问并适用于方法调用,则必须选择一个为运行时方法调度提供描述符。 Java编程语言使用选择最具体方法的规则。
在Java和Scala中也是如此:在编译时选择使用哪个重载(然后在运行时将此特定重载调度,如报价说)。由于e.left_arg
不知道是ExpAdd
还是ExpSub
,因此编译器只能选择interpret(exp: Exp)
并在删除时抱怨。一种选择是删除其他重载:
def interpret(exp: Exp) = exp match {
case e : ExpAdd => interpret(e.left_arg) + interpret(e.right_arg)
case e : ExpSub => interpret(e.left_arg) - interpret(e.right_arg)
}
// when extending
override def interpret(exp: Exp) = exp match {
case e : ExpMult => interpret(e.left_arg) * interpret(e.right_arg)
case _ => super.interpret(exp)
}