如何在编译时失败而不是运行时异常

时间:2014-09-22 01:01:49

标签: scala implicit-conversion

我有这个简单的代码

def doStuff(x: Int) = {
 ...
}

但是,如果x小于或等于0,则此方法不支持,因此我使用require

def doStuff(x: Int) = {
   require(x > 0, "x should be greater than 0")
}

doStuff(0) // runtime exception

但这发生在运行时异常中,如何更改它以使用Implicit在编译时显示错误?

非常感谢提前

2 个答案:

答案 0 :(得分:10)

在一般情况下,这绝对不可能 - 编译器没有足够的关于任意Int的信息。不过,你确实有几个选择。如果您愿意将参数作为文字,则可以使用宏:

import scala.language.experimental.macros, scala.reflect.macros.Context

def doStuffImpl(c: Context)(x: c.Expr[Int]): c.Expr[Unit] = {
  import c.universe._

  x.tree match {
    case Literal(Constant(xLit: Int)) if xLit > 0 => c.literalUnit
    case Literal(Constant(xLit: Int)) =>
      c.abort(c.enclosingPosition, "x must be > 0")
    case _ => c.abort(c.enclosingPosition, "x must be a literal")
  }
}

def doStuff(x: Int) = macro doStuffImpl

然后:

scala> doStuff(1)

scala> doStuff(0)
<console>:12: error: x must be > 0
              doStuff(0)
                     ^

scala> val y = 1
y: Int = 1

scala> doStuff(y)
<console>:13: error: x must be a literal
              doStuff(y)
                     ^

也可以使用类似Shapeless的类型级自然数字:

import shapeless._, nat._, ops.nat.{ Diff, LT }

def doStuff(n: Nat)(implicit lt: LT[_0, n.N]) = ()

现在你可以做更复杂的事情:

def doStuffWithDiff[C <: Nat](a: Nat, b: Nat)(implicit
  sum: Diff.Aux[a.N, b.N, C],
  lt: LT[_0, C]
) = doStuff(Witness.witnessN[C].value)

现在doStuffWithDiff(10, 10)doStuffWithDiff(10, 11)无法编译,但doStuffWithDiff(10, 9)将会编译。

这是一种dependently typed programming,它可以非常强大,但是尝试在Scala中执行它(如果你真的很眯眼,它只是一种依赖类型的编程语言)会引入相当多的数量语法开销,所以你可能只想在编译时正确性非常高的情况下使用这种方法。

答案 1 :(得分:0)

最常见的替代方法是定义一个简单的包装类PositiveInt(或任何最适合的名称),它使运行时检查。您的功能将始终具有预期的功能......