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