使用类型对任意约束建模以进行编译时检查 [英] Using types to model arbitrary constraints for compile-time checking

查看:86
本文介绍了使用类型对任意约束建模以进行编译时检查的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑到Scala的强类型系统,我有一个雄心勃勃的项目,现在我打算放弃该项目,因为它的使用率似乎太高了.

Given the strong type system of Scala, I had an ambitious project which I'm about to abandon now because the effort to usefulness ratio seems to be too high.

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

Basically I have some graph elements (GE) and they correspond to sound processes which are carried out at a given calculation rate. Graph elements are composed from other graph elements forming their inputs. Now there are rather arbitrary constraints on the inputs' rates. In the source language (SuperCollider) the rates are checked at runtime, naturally because it's a dynamically typed language. I wanted to see if I can enforce the check at compile time.

一些约束相当简单,可以用"arg1的速率必须至少与arg2的速率一样高"的形式表示.但是其他人会变得错综复杂,例如

Some constraints are fairly simply and can be expressed in the forms of "rate of arg1 must be at least as high as rate of arg2". But others get intricate, e.g.

如果arg0的费率是'demand',则args1的费率必须是'demand'或'scalar'或等于封闭的GE的费率".

"if arg0's rate is 'demand', args1's rate must be either 'demand' or 'scalar' or equal to the enclosing GE's rate".

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

The question is: Should I give up on this? Here is how it looks with runtime check:

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

与此相反,使用速率的类型参数可以看到它:

And in constrast how it could look with type parameters for the rates:

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

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

A path worth pursuing? Three more things that speak against it, apart from the code bloat:

  • 可能有几十个GE需要自定义约束
  • 编写GE变得越来越困难:代码可能需要传递数十个类型参数
  • 转换可能会变得困难,例如想象一个List[GE[_<:Rate]].map( ??? ).我的意思是Duty.RateCons如何转换为TDuty.RateCons(其中TDuty是不同的GE)...
  • There are probably a couple of dozen GEs which would need custom constraints
  • Composing GEs becomes increasingly difficult: code might need to pass around dozens of type parameters
  • Transformations might become difficult, e.g. imagine a List[GE[_<:Rate]].map( ??? ). I mean how would Duty.RateCons translate to TDuty.RateCons (where TDuty is a different GE)...

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

I had invested quite a bit of time in this project already, that's why I'm reluctant to give up so easily. So... convince me that I'm doing something useful here, or tell me that I should go back to the dynamically checked version.

推荐答案

正如Jesper Nordenberg提到的,要做的事情是定义一组封闭的类型,并对这些类型进行相等操作.如果您确实要解决此问题,那么将需要一个有关如何解决该问题的示例.同样,希望有一个发问者需要的类型级别的编程示例.

As mentioned by Jesper Nordenberg, the thing to do is define a closed set of types and an equality operation over those types. If you do revisit this problem, an example of how you solved it would be desirable. Also, an example of type-level programming of the sort required by the questioner is desirable.

此处中了解更多此处

这篇关于使用类型对任意约束建模以进行编译时检查的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆