鑑於Scala的強類型系統,我有一個雄心勃勃的項目,我現在要放棄,因爲有用比率的努力似乎太高。使用類型爲編譯時檢查建立任意約束
基本上我有一些圖表元素(GE
),它們對應於以給定的計算速率執行的聲音處理。圖形元素由其他圖形元素組成,形成其輸入。現在對輸入的費率有相當隨意的限制。在源語言(SuperCollider)中,速率在運行時被檢查,因爲它是一種動態類型的語言。我想看看我是否可以在編譯時強制執行檢查。
一些約束相當簡單,可以用「arg1的比率必須至少與arg2的比率一樣高」的形式來表示。但其他人則錯綜複雜,例如
「如果arg0的費率是'demand',args1的費率必須是'demand'或'scalar'或等於封閉的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))
}
而且在constrast怎麼會看與類型參數率:
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
S的這將需要自定義約束, - 撰寫
GE
S成爲越來越困難:代碼可能需要繞過幾十個類型參數 - 轉換可能會變得困難,例如想象一下
List[GE[_<:Rate]].map(???)
。我的意思是如何將Duty.RateCons
轉化爲TDuty.RateCons
(其中TDuty
是不同的GE
)...
我已經投入了相當多的時間在這個項目中已,這就是爲什麼我不願意這麼輕易放棄。所以...說服我,我在這裏做一些有用的事,或者告訴我應該回到動態檢查版本。
我也會對一些資源感興趣,這些資源給出了一般建議,哪種斷言實際上可以靜態檢查。類似於設計模式。我更喜歡它是爲斯卡拉,但不一定。 – ziggystar 2011-04-06 12:48:27
@ziggystar我發現以下有用:http://michid.wordpress.com/2008/10/29/meta-programming-with-scala-conditional-compilation-and-loop-unrolling/; HTTP://apocalisp.wordpress。com/2010/06/13/type-level-programming-in-scala-part-3-boolean /:http://jnordenberg.blogspot.com/2009/09/type-lists-and-heterogeneously-typed.html ;從第一個鏈接中得到這樣的陳述:「這意味着類型集(這將是一個有用的構造)在Scala中幾乎不可能創建。」 - 讓我想到我的問題的答案是:保持動態的方法... – 2011-04-06 14:14:01
我的聲明是,任意類型的集合(當前)不可能在Scala中創建,但是如果您可以控制集合中的類型您可以創建一個可用於創建集合類型的類型級別相等函數。 – 2011-04-07 14:11:55