如何在Scala中使用继承类型参数化函数

时间:2018-01-27 11:14:50

标签: scala compilation

让我们说我已定义了这些类(这是我的代码的简化版本)

sealed trait Expression

trait ExpressionA extends Expression
trait ExpressionB extends Expression

case class OperationA(op: String, a: ExpressionA, b:ExpressionA) extends ExpressionA
case class OperationB(op: String, a: ExpressionB, b:ExpressionB) extends ExpressionB
case class ComparisonB(op: String, a: ExpressionA, b:ExpressionA) extends ExpressionB
case class VariableA(op: String, a: ExpressionA) extends ExpressionA

然后我想要替换VariableA类中的给定字符串 所以我可以写

def replace(exp: ExpressionB, operation: String => String): ExpressionB = exp match {
  case ComparisonB(op, a, b) => ComparisonB(op, replace(a, operation), replace(b, operation))
  case OperationB(op, a, b) => OperationB(op, replace(a, operation), replace(b, operation))

}

def replace(exp: ExpressionA, operation: String => String): ExpressionA = exp match {
  case OperationA(op, a, b) => OperationA(op, replace(a, operation), replace(b, operation))
  case VariableA(name) => VariableA(operation(name))
}

但我想将这两个函数合并为一个泛型类型。

这是我试过的

def replace[T <: Expression](exp:T, operation: String => String):T = exp match {
  case ComparisonB(op, a, b) => ComparisonB(op, replace(a, operation), replace(b, operation))
  case OperationB(op, a, b) => OperationB(op, replace(a, operation), replace(b, operation))
  case OperationA(op, a, b) => OperationA(op, replace(a, operation), replace(b, operation))
  case VariableA(name) => VariableA(operation(name))
}

我收到错误

Expression of type ExpressionA does not conform to expected type T

尽管理论上它总是和输入的类型相同。所以我不知道我的代码有什么问题,它是否缺少一些含义,或者错误的方法呢?

2 个答案:

答案 0 :(得分:2)

如果您希望replace的返回类型依赖于exp的值(即exp是否与特定模式匹配),那么实际上您需要polymorphic函数。

  trait Replacer[T <: Expression] {
    def replace(exp: T, operation: String => String): T
  }

  object Replacer {
    implicit val exprA: Replacer[ExpressionA] =
      (exp: ExpressionA, operation: String => String) => exp match {
        case OperationA(op, a, b) => OperationA(op, replace(a, operation), replace(b, operation))
        case VariableA(name) => VariableA(operation(name))
      }

    implicit val exprB: Replacer[ExpressionB] =
      (exp: ExpressionB, operation: String => String) => exp match {
        case ComparisonB(op, a, b) => ComparisonB(op, replace(a, operation), replace(b, operation))
        case OperationB(op, a, b) => OperationB(op, replace(a, operation), replace(b, operation))
      }
  }

  def replace[T <: Expression](exp: T, operation: String => String)(implicit replacer: Replacer[T]): T =
    replacer.replace(exp, operation)

答案 1 :(得分:1)

在Scala match表达式中,返回所有情况的公共类型。在示例中,您提供的常见类型为Expression而不是T,因此会出现编译错误。

您可以将返回类型定义为Expression或(如果要保留传递给方法的类型,请将替换保存为Expression的实例方法,即def replace(operation: String => String): Expression并覆盖它在类中替换返回类型,例如def replace(operation: String => String): OperationA