【问题标题】:How can I enforce compile-time constraints on values for Scala methods?如何对 Scala 方法的值强制执行编译时约束?
【发布时间】:2017-01-14 07:29:17
【问题描述】:

我想在编译时强制约束 Scala 方法的参数值。

例如:

case class Foo(numberOfFoo: Int, ...)

numberOfFoo 是上面的Int,但我真的很想让它成为一个正整数。我已经尝试过诸如 PositiveInt 之类的类来强制执行此操作,但这只是将检查推送到另一个仍然没有在编译时检查的类。

使用上面的例子,我想要这个:

val n: Int = ...
val f: Foo = Foo(n)

如果n > 0 则编译,如果n <= 0 则不编译。我不希望实例化代码必须处理可能的异常,处理Option[Foo],或者以Foo 结束Foo.numberOfFoo != n(即我不想使用输入参数的绝对值)。

更新:感谢您提供有用的信息。正如我所担心的那样。大多数情况下,我希望能够指定必须具有正整数大小的东西的大小。所以这似乎是最好的方法:

case class Foo(bar: Bar) {val n = bar size}

【问题讨论】:

  • 这在 Scala 中是不可能的,因为它不支持值依赖类型(至少在您需要的范围内)。

标签: scala constraints compile-time


【解决方案1】:

您将不得不使用refined 库。这是不诉诸Nat 或其他类型技巧的唯一方法。从自述文件中的示例:

import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric._

// This refines Int with the Positive predicate and checks via an
// implicit macro that the assigned value satisfies it:
scala> val i1: Int Refined Positive = 5
i1: Int Refined Positive = 5

// If the value does not satisfy the predicate, we get a meaningful
// compile error:
scala> val i2: Int Refined Positive = -5
<console>:22: error: Predicate failed: (-5 > 0).
       val i2: Int Refined Positive = -5

【讨论】:

  • 出于好奇,你有什么理由选择这个答案而不是PosInt
  • @KevinMeredith 这是自述文件中的第一个示例……哦,那是在 Scalastic 中的吗?我不熟悉使用它。但是,我对这个库很熟悉。对于这个用例,我不能说一个比另一个更好。
【解决方案2】:

另一种方法是使用shapeless 库和Nat。限制是您需要在编译时使用已知常量来实例化那些 Foo 实体。

import shapeless.ops.nat_
import shapeless.nat._

case class Foo[T <: Nat](numberOfFoo: Int)(implicit ev: GT[T, _0]

object Foo {
  // The problem is this won't work.
  def apply[T <: Nat](n: Int): Foo[T] = Foo(Nat(n))
}

只有这样使用才会起作用:

Foo(_1)

_1 来自shapeless.nat._。如果您深入了解实现,0 部分恰好被强制执行,即使您无意通过 shapeless:

 if (n < 0) c.abort(c.enclosingPosition, s"A Nat cannot represent $n")

坚持简单的事情

然而这相当麻烦,因为无论你采用哪种方法,它都将依赖于宏,而宏不能真正起作用,除非在编译时知道值。如果最简单的委托方法不再有效,这可能会变得非常有限。

在实践中,绕过这种方法并使用常规方法可能更有效。无论您使用 shapeless 库还是上面提到的精炼库,故事都不会改变,因此对于正常用例,进行运行时验证可能更简洁。

【讨论】:

    猜你喜欢
    • 2011-07-19
    • 2012-12-05
    • 2023-04-08
    • 1970-01-01
    • 2011-09-07
    • 2016-01-15
    • 1970-01-01
    • 2015-07-13
    • 1970-01-01
    相关资源
    最近更新 更多