对于我正在从事的项目,我想创建一个代表资源数量的类。将来它可能会成为不同资源类型计数的集合(因此不将 Resources 类本身编码为 Value 类),但现在单个(匿名)资源类型就足够了。
但是,此资源计数不应为负数,因此我想将其可以具有的值限制为自然数集(即非负整数)。为此,我正在考虑创建一个名为 Nat 的新值类。
我想要实现的几个语义点:
- 如果您尝试从负值创建 Nat,您应该会抛出异常。
- 如果您尝试将 Int(或另一个 Nat)添加到现有 Nat,它应该可以工作,如果传入的 int 是一个足够大的负数,则将值截断为零 - 不会抛出异常!
这意味着除了之外def +(nat: Nat)
,我还想要某种形式的def +(int: Int)
,否则传递给的 Int+
将首先转换为 Nat,这可能会导致异常。但是,因为 Nat 是一个 Value 类,所以这两个方法在擦除后将具有相同的签名,所以这不起作用。
我也尝试过def +(int: RichInt)
,希望隐式转换优先,但 RichInt 也是一个 Value 类,因此会出现同样的问题。
我确实发现的一种解决方法是使用混入 RichInt 的特性之一,特别是 OrderedProxy。现在 Int 将被隐式转换为 RichInt 并作为 OrderedProxy 传递给此方法(在这种形式下,它不被识别为 Value 类),而不是被转换为 Nat,我得到了我想要的语义。
因此,到目前为止,我的代码如下所示:
import runtime.{IntegralProxy, OrderedProxy}
class Nat private(val self: Int) extends AnyVal with IntegralProxy[Int]
{
protected def num = scala.math.Numeric.IntIsIntegral
protected def ord = scala.math.Ordering.Int
import Nat._
def isZero = (this == Zero)
def +(nat: Nat): Nat = Nat(self + nat.self)
def +(int: OrderedProxy[Int]): Nat = trunc(self + int.self)
def -(nat: Nat): Nat = trunc(self - nat.self)
def -(int: OrderedProxy[Int]): Nat = trunc(self - int.self)
def -%(nat: Nat) = (this - nat).self match { // Create a tuple with the reduced count of the minuend, plus any remainder from the subtrahend if the minuend is now zero.
case 0 => (Zero, (nat - this))
case nonZero => (Nat(nonZero), Zero)
}
}
object Nat
{
val NEG_PARAM_MSG = "Cannot assign negative value"
val Zero: Nat = Nat(0)
def apply(value: Int): Nat = value match {
case cnt if (cnt < 0) => throw new RuntimeException(NEG_PARAM_MSG)
case 0 => Zero
case cnt => new Nat(cnt)
}
def apply(value: Long): Nat = apply(value.toInt)
def trunc(value: Int): Nat = value match {
case cnt if (cnt <= 0) => Zero
case cnt => new Nat(cnt)
}
def trunc(value: Long): Nat = trunc(value.toInt)
}
trait ResourcesComponent
{
import Nat._
sealed case class Resources(count: Nat)
{
import Resources._
require(count != Zero || hasNone)
def hasNone = (this == none)
def +(res: Resources) = Resources(count + res.count)
def -(res: Resources) = Resources(count - res.count)
def -%(res: Resources) = (count - res.count).self match { // Similar to -% for Nat, but convert to a tuple of Resources - is there a better (eg. '.map'-like) way to do this?
case 0 => (none, Resources(res.count - count))
case leftOver => (Resources(leftOver), none)
}
}
object Resources
{
val NEG_RES_MSG = "Cannot assign negative resources"
def apply(value: OrderedProxy[Int]) = value.self match {
case cnt if (cnt < 0) => throw new RuntimeException(NEG_RES_MSG)
case 0 => none
case cnt => new Resources(Nat(cnt))
}
object none extends Resources(Zero)
{
override def hasNone = true
override def +(res: Resources) = res
override def -(res: Resources) = none
override def -%(res: Resources) = (none, res)
}
}
}
正如我所说,它似乎有效,但周围的工作感觉有点笨拙。有什么改进的建议吗?