4

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)
}

追求する価値のある道?コードの肥大化を除けば、さらに 3 つの反対意見があります。

  • GEカスタム制約が必要ながおそらく数十個あります
  • sの構成GEはますます難しくなります: コードは数十の型パラメータを渡す必要があるかもしれません
  • 変換が難しくなる場合があります。たとえば、 を想像してみてくださいList[GE[_<:Rate]].map( ??? )。私はどのようにDuty.RateCons翻訳するかを意味しますTDuty.RateCons(どこTDutyが違うのですかGE)...

私はすでにこのプロジェクトにかなりの時間を費やしてきたので、簡単にあきらめるのは気が進まないのです。だから...私がここで何か役に立つことをしていると私に納得させるか、動的にチェックされたバージョンに戻るべきだと言ってください。

4

1 に答える 1

0

Jesper Nordenberg が述べたように、すべきことは、型の閉じたセットとそれらの型に対する等値演算を定義することです。この問題を再検討する場合は、どのように解決したかの例が望ましいでしょう。また、質問者が必要とするタイプレベルのプログラミングの例が望ましい。

詳細はこちらこちらをご覧ください。

于 2011-04-29T20:10:27.690 に答える