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中找到了一种方法。这是我的原型