使用类型为编译时检查建模任意约束

时间:2011-04-05 20:25:09

标签: scala type-safety type-constraints supercollider

鉴于Scala强大的类型系统,我有一个雄心勃勃的项目,我现在要放弃了,因为实用率的努力似乎太高了。

基本上我有一些图形元素(GE),它们对应于以给定计算速率执行的声音处理。图形元素由形成其输入的其他图形元素组成。现在输入的费率有相当随意的约束。在源语言(SuperCollider)中,速率在运行时被检查,当然因为它是动态类型语言。我想看看我是否可以在编译时强制执行检查。

一些约束相当简单,可以用“arg1的速率必须至少与arg2的速率一样高”的形式表达。但其他人则错综复杂,例如。

“如果arg0的费率是'需求',args1的费率必须是'需求'或'标量'或等于封闭的GE的费率”。

问题是:我应该放弃这个吗?以下是运行时检查的外观:

sealed trait Rate
case object demand  extends Rate
case object audio   extends Rate
case object control extends Rate
case object scalar  extends Rate

trait GE { def rate: Rate }

// an example GE:
case class Duty(rate: Rate, in0: GE, in1: GE) extends GE {
  def checkRates(): Unit =
    require(in0.rate != demand || (in1.rate != demand &&
            in1.rate != scalar && in1.rate != rate))
}

并且它与费率的类型参数有什么不同:

sealed trait Rate
trait audio   extends Rate
trait demand  extends Rate
trait control extends Rate
trait scalar  extends Rate

trait GE[R <: Rate]

object Duty {
  trait LowPri {
    implicit def con1[R, T]: RateCons[R, audio  , T] = new ConImpl[R, audio  , T]
    implicit def con2[R, T]: RateCons[R, control, T] = new ConImpl[R, control, T]
    implicit def con3[R, T]: RateCons[R, scalar , T] = new ConImpl[R, scalar , T]

    implicit def con4[R, T]: RateCons[R, demand , demand] = 
      new ConImpl[R, demand, demand]

    implicit def con5[R, T]: RateCons[R, demand , scalar] = 
      new ConImpl[R, demand, scalar]
  }
  object RateCons extends LowPri {
    implicit def con6[R]: RateCons[R, demand, R] = new ConImpl[R, demand, R]
  }
  private class ConImpl[ R, S, T ] extends RateCons R, S, T ]
  sealed trait RateCons[ R, S, T ]

  def ar[S <: Rate, T <: Rate](in0: GE[S], in1: GE[T])(
    implicit cons: RateCons[audio, S, T]) = apply[audio, S, T](in0, in1)

  def kr[S <: Rate, T <: Rate](in0: GE[S], in1: GE[T])( 
    implicit cons: RateCons[control, S, T]) = apply[control, S, T](in0, in1)
}
case class Duty[R <: Rate, S <: Rate, T <: Rate](in0: GE[S], in1: GE[T])(
  implicit con: Duty.RateCons[R, S, T]) extends GE[R]

试验:

def allowed(a: GE[demand], b: GE[audio], c: GE[control], d: GE[scalar]): Unit = {
  Duty.ar(b, c)
  Duty.kr(b, c)
  Duty.ar(b, a)
  Duty.ar(b, d)
  Duty.ar(a, b)
  Duty.kr(a, c)
}

def forbidden(a: GE[demand], b: GE[audio], c: GE[control], d: GE[scalar]): Unit = {
  Duty.kr(a, b)
  Duty.ar(a, c)
}

值得追求的道路?除了代码膨胀之外,还有三件事情反对它:

  • 可能有几十个GE需要自定义约束
  • 组建GE变得越来越困难:代码可能需要传递数十种类型参数
  • 转换可能会变得困难,例如想象一下List[GE[_<:Rate]].map( ??? )。我的意思是Duty.RateCons将如何转换为TDuty.RateCons(其中TDuty是不同的GE)...

我已经在这个项目上投入了相当多的时间,这就是为什么我不愿意轻易放弃的原因。所以...说服我在这里做一些有用的事情,或者告诉我应该回到动态检查的版本。

1 个答案:

答案 0 :(得分:0)

正如Jesper Nordenberg所提到的,要做的是定义一组封闭的类型和对这些类型的相等操作。如果你重新审视这个问题,你需要一个如何解决它的例子。此外,还需要提问者所需类型的类型级编程示例。

了解更多herehere