我不知道如何静态地做到这一点,除了依赖类型(example),这是 Scala 没有的。如果您只处理常量,则应该可以使用宏或编译器插件来执行必要的检查,但如果您有任意浮点类型的表达式,您很可能不得不求助于运行时检查。
这是一种方法。定义一个执行运行时检查以确保浮点值在所需范围内的类:
abstract class AbstractRangedFloat(lb: Float, ub: Float) {
require (lb <= value && value <= ub, s"Requires $lb <= $value <= $ub to hold")
def value: Float
}
您可以按如下方式使用它:
case class NormalisedFloat(val value: Float)
extends AbstractRangedFloat(0.0f, 1.0f)
NormalisedFloat(0.99f)
NormalisedFloat(-0.1f) // Exception
或者作为:
case class RangedFloat(val lb: Float, val ub: Float)(val value: Float)
extends AbstractRangedFloat(lb, ub)
val RF = RangedFloat(-0.1f, 0.1f) _
RF(0.0f)
RF(0.2f) // Exception
如果可以使用值类来获得一些性能,那就太好了,但是requires
构造函数中的调用(当前)禁止这样做。
编辑: @paradigmatic 发表评论
这是一个直观的论点,为什么可以在不(完全)支持依赖类型的类型系统中对依赖于自然数的类型进行编码,但范围浮点数可能不能: 自然数是可枚举的集合,这使得对每个元素进行编码成为可能作为使用 Peano 数字的路径相关类型。然而,实数不再是可枚举的,因此不再可能系统地创建对应于实数的每个元素的类型。
现在,计算机浮点数和实数最终都是有限集,但仍然要大到可以在类型系统中合理有效地枚举。计算机自然数的集合当然也非常大,因此对编码为类型的 Peano 数字的算术提出了问题,请参阅本文的最后一段。但是,我声称使用前n(对于相当小的n)自然数通常就足够了,例如,HLists证明了这一点。对浮点数做出相应的声明不太令人信服——在 0.0 和 1.0 之间编码 10,000 个浮点数,或者在 0.0 和 100.0 之间编码 10,000 个浮点数会更好吗?