Scala 我应该使用隐式转换来强制执行前提条件吗?

Scala 我应该使用隐式转换来强制执行前提条件吗?,scala,Scala,我突然想到,我可以使用隐式转换来宣布和强制预条件。考虑这一点: object NonNegativeDouble { implicit def int2nnd(d : Double) : NonNegativeDouble = new NonNegativeDouble(d) implicit def nnd2int(d : NonNegativeDouble) : Double = d.v def sqrt(n : NonNegativeDouble) : NonNegative

我突然想到,我可以使用隐式转换来宣布和强制预条件。考虑这一点:

object NonNegativeDouble {
  implicit def int2nnd(d : Double) : NonNegativeDouble = new NonNegativeDouble(d) 
  implicit def nnd2int(d : NonNegativeDouble) : Double = d.v
  def sqrt(n : NonNegativeDouble) : NonNegativeDouble = scala.math.sqrt(n)
}

class NonNegativeDouble(val v : Double ) {
  if (v < 0) {
    throw new IllegalArgumentException("negative value")
  }
}

object Test {
  def t1 = {
    val d : Double = NonNegativeDouble.sqrt(3.0);
    printf("%f\n", d);
    val n : Double = NonNegativeDouble.sqrt(-3.0);
  }
}
val a = UnsignedInt(5)
val b = a - 3 // now, b should be an UnsignedInt(2)
val c = b - 3 // now, c must be an Int, because it's negative!
对象非负双倍{
隐式def int2nnd(d:Double):NonNegativeDouble=新的NonNegativeDouble(d)
隐式def nnd2int(d:NonNegativeDouble):Double=d.v
def sqrt(n:NonNegativeDouble):NonNegativeDouble=scala.math.sqrt(n)
}
类非负双(v值:双){
if(v<0){
抛出新的IllegalArgumentException(“负值”)
}
}
对象测试{
def t1={
val d:Double=非负双倍.sqrt(3.0);
printf(“%f\n”,d);
val n:Double=非负双倍.sqrt(-3.0);
}
}
暂时忽略这个例子的实际空白:我的观点是,子类NonNegativeDouble表达了这样一个概念,即函数只接受整个类值范围的一个子集

首先是:

  • 好主意
  • 坏主意,或
  • 一个大家都知道的显而易见的想法

  • 其次,这对于基本类型(如Int和String)最有用。当然,这些类都是最终类,那么是否有一种好方法不仅可以在函数中使用受限类型(这就是第二个隐式函数的用途),还可以将基础值委托给所有方法(不必手动实现每个委托)?

    实际上,这是一个非常好的主意,虽然我不会在任何性能敏感的循环中使用它


    @在这里,专业化也可以起到相当大的作用,使代码更加高效……

    这是一个非常酷的想法,但不幸的是,它的真正潜力无法在Scala的类型系统中实现。这里真正需要的是,它允许您对方法的调用方施加一个证明义务,以验证参数是否在范围内,这样方法甚至不能用无效参数调用

    但是,如果没有依赖类型和在编译时验证规范的能力,我认为这有值得怀疑的价值,甚至撇开性能考虑。考虑一下,如何比使用函数来声明方法所需的初始条件更好,比如:

    def foo(i:Int) = {
        require (i >= 0)
        i * 9 + 4
    }
    
    在这两种情况下,负值都会导致在运行时抛出异常,无论是在
    require
    函数中还是在构造
    非负双倍时。这两种技术都清楚地说明了方法的契约,但我认为在构建所有这些专用类型时会有很大的开销,这些专用类型的唯一目的是封装要在运行时断言的特定表达式。例如,如果你想强制执行一个稍微不同的前提条件,该怎么办;喂,那是
    i>45
    ?您是否只为该方法构建一个大于45的
    类型

    我能看到的关于构建例如
    nonnegativefo
    类型的唯一参数是,如果您有许多只使用和返回正数的方法。即便如此,我认为回报还是值得怀疑的


    顺便说一句,这与我给出了类似答案的问题类似。

    这在C中通常被称为“unsigned int”。我认为这不是很有用,因为您无法正确定义运算符。考虑这一点:

    object NonNegativeDouble {
      implicit def int2nnd(d : Double) : NonNegativeDouble = new NonNegativeDouble(d) 
      implicit def nnd2int(d : NonNegativeDouble) : Double = d.v
      def sqrt(n : NonNegativeDouble) : NonNegativeDouble = scala.math.sqrt(n)
    }
    
    class NonNegativeDouble(val v : Double ) {
      if (v < 0) {
        throw new IllegalArgumentException("negative value")
      }
    }
    
    object Test {
      def t1 = {
        val d : Double = NonNegativeDouble.sqrt(3.0);
        printf("%f\n", d);
        val n : Double = NonNegativeDouble.sqrt(-3.0);
      }
    }
    
    val a = UnsignedInt(5)
    val b = a - 3 // now, b should be an UnsignedInt(2)
    val c = b - 3 // now, c must be an Int, because it's negative!
    
    因此,如何定义减号运算符?也许像这样:

    def -(i:Int):Either[UnsignedInt,Int]
    
    这将使带有
    UnsignedInt
    的算术实际上无法使用

    或者定义一个超类,
    可以是SignedInt
    ,它有两个子类,
    SignedInt
    UnsignedInt
    。然后您可以在
    UnsignedInt
    中定义减法,如下所示:

    def -(i:Int):MaybeSignedInt
    

    看起来很糟糕,不是吗?实际上,从概念上讲,数字的符号不应该是数字类型的属性,而应该是数字值的属性。

    在不回答任何实际问题的情况下,让我回答你简单的问题。首先,为什么这样做而不是
    要求
    ?我将忽略我从未听说过的基本事实,并提及(a)这种方式更确定、更简洁,(b)这种方式与功能用户沟通,而不是假设他阅读了手册,而他没有阅读。第二,我想
    能够将范围限制表示为
    NumberGreaterThan[45]
    ,但我认为我不能。嘿,从这个角度来看,C++比斯卡拉好!我们应该写信给Bjorn Stroustrop并告诉他,
    无符号int
    的C概念不是
    int
    的限定范围。例如,2147483649是一个无符号整数(在32位机器中),但它不是整数。算术运算在子集上是不闭合的,因此在我的系统中,两个非负元素上的负数甚至返回一个整数。实际上,在任何计算机语言中,它们在常规数字类型上被视为闭合的唯一方法是扩展该类型(包括
    NaN
    )和接受只能被描述为错误答案的内容(如溢出、下溢和精度损失).你为执行合同付出了高昂的代价。除非你能从中获得更多好处,否则我一般不会推荐这种做法。我在csharp中找到了一种方法。这是我的原型