Использование типов для моделирования произвольных ограничений для проверки во время компиляции - PullRequest
4 голосов
/ 06 апреля 2011

Учитывая строгую систему типов 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 s, для которых потребуются пользовательские ограничения
  • Составление GE s становится все более сложным: код может нуждаться в передаче десятков параметров типа
  • Преобразования могут стать трудными, например, представьте себе List[GE[_<:Rate]].map( ??? ). Я имею в виду, как бы Duty.RateCons перевести на TDuty.RateCons (где TDuty - это другое GE) ...

Я уже потратил немало времени на этот проект, поэтому я не хочу так легко сдаваться. Итак ... убедите меня, что я делаю что-то полезное здесь, или скажите, что мне следует вернуться к динамически проверенной версии.

1 Ответ

0 голосов
/ 30 апреля 2011

Как упоминал Джеспер Норденберг, нужно определить закрытый набор типов и операцию равенства над этими типами. Если вы действительно вернетесь к этой проблеме, пример того, как вы ее решили, был бы желателен. Кроме того, желателен пример программирования на уровне типов того типа, который требуется спрашивающему.

Подробнее здесь и здесь .

...