具有非固定值类型集的 Scala DSL

Scala DSL with non-fixed set of value types

我正在尝试设计一个 DSL,它不固定在它可以作为值支持的类型中。

下面我尝试使用 Value 类型类来实现这一点。它没有行为,但它会在预期的应用程序中。

trait Value[T]
object Value {
  implicit object IntIsValue extends Value[Int]
  implicit object StringIsValue extends Value[String]
}

DSL由值项和应用项组成:

abstract class Term[T: Value]
case class ValueTerm[T: Value](x: T) extends Term[T]
case class AppTerm[Arg: Value, T: Value](fun: Arg => T, arg: Term[Arg]) extends Term[T]

求值函数是我遇到编译问题的地方:

def eval[T: Value](term: Term[T]): T = {
  term match {
    case ValueTerm(x) => x
    case AppTerm(fun, arg) => fun(eval(arg)) // doesn't compile
  }
}

这是我得到的代表性编译错误:

Error:(14, 40) could not find implicit value for evidence parameter of type A$A354.this.Value[Any]
    case AppTerm(fun, arg) => fun(eval(arg))
                                      ^

所以编译器认为 argTerm[Any] 而不知道它是 Value.

的一个实例

我知道我可以通过从 eval 中删除 Value 约束来避免它。但是,然后我失去了 Value 的行为,我可能想在 eval:

中使用
def eval[T](term: Term[T]): T -- loses behaviour of Value typeclass

所以我的问题是

  1. 为什么这不能编译,并且
  2. 如何实现这样的目标

下面是 DSL 的一些用法:

val i41: Term[Int] = ValueTerm(41)
val i42: Term[Int] = AppTerm(fun = (_: Int) + 1, arg = i41)
val theAnswer: Term[String] = AppTerm(fun = "The answer is " ++ (_: Int).toString, arg = i42)

eval(i41)
eval(i42)
eval(theAnswer)

问题是这些术语带有必要的隐式,但这样它们就不会自动成为隐式范围的一部分。一个快速修复方法是向 AppTerm 添加一个公开 Value[Arg] 的方法。然后您可以将其显式传递给 eval

def eval[T: Value](term: Term[T]): T = {
  term match {
    case ValueTerm(x) => x
    case t @ AppTerm(fun, arg) => fun(eval(arg)(t.implicitArg))
  }
}

但是,您可能会将此视为您想要设计稍微不同的解决方案的标志。例如,您在 Term 中捕获隐式 Value ,然后在 eval 中传入 Term 并再次传递隐式 [=14= 似乎很危险].因此,可以将与 Term.

中捕获的 Value 不同的 eval 传递给 eval