Scala DSL具有非固定值集类型

时间:2017-11-01 18:18:29

标签: scala dsl

我试图设计一个没有固定在它可以作为值支持的类型的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的实例。

我知道我可以通过从Value删除eval约束来避免这种情况。但是,我失去了Value的行为,我可能想在eval中使用:

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

所以我的问题是

  1. 为什么这不编译,
  2. 如何实现这样的目标
  3. 以下是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)
    

1 个答案:

答案 0 :(得分:1)

问题在于这些术语带有必要的含义,但这样它们不会自动成为隐式范围的一部分。快速解决方法是向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))
  }
}

但是,您可能会将此视为您希望以不同方式设计解决方案的标志。例如,您在Value中捕获隐式Term,然后在eval中捕获Term并且再次隐含Value似乎很危险}。因此,可以传递与Value中捕获的eval不同的Termobj.results