Scala 为编译时文字和 运行-time 变量优化了整数

Scala refined integer for both compile-time literal and run-time variable

我希望将变量限制为布尔整数表示形式(0 或 1),作为定义的输入。到目前为止,这可以通过两种方式实现,一种在运行时,一种在编译时,仅用于文字。

是否有可能以某种方式将两者结合起来,这样我就可以创建一个类型,在编译时拒绝超出范围的文字值,但也允许在运行时检查非文字输入?

运行时守卫

类似于此博客 post: http://erikerlandson.github.io/blog/2015/08/18/lightweight-non-negative-numerics-for-better-scala-type-signatures/

/////////////////////////////
//Runtime guard for boolean
/////////////////////////////
object zero_or_one {
  import scala.language.implicitConversions

  class ZeroOrOneRuntime private (val value: Int) extends AnyVal

  object ZeroOrOneRuntime {
    def apply(v: Int) = {
      require(v == 0 || v == 1, "0 or 1 accepted only")
      new ZeroOrOneRuntime(v)
    }

    implicit def toZeroOrOneRuntime(v: Int) = ZeroOrOneRuntime(v)
  }

  implicit def toInt(nn: ZeroOrOneRuntime) = nn.value
}

import zero_or_one._

var a : ZeroOrOneRuntime = 0
val a_bad :ZeroOrOneRuntime = 2 //java.lang.IllegalArgumentException: requirement failed: 0 or 1 accepted only

for (i <- 0 to 10)
  a = i //java.lang.IllegalArgumentException: requirement failed: 0 or 1 accepted only

编译时保护(仅限文字)

通过使用 scala 精化库 https://github.com/fthomas/refined

//////////////////////////////////
//Compile-time guard for boolean
//////////////////////////////////
import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric._

type ZeroOrOneLiteral = Int Refined Interval.Closed[W.`0`.T, W.`1`.T]

var b : ZeroOrOneLiteral = 1
val b_bad : ZeroOrOneLiteral = 2 //Right predicate of (!(2 < 0) && !(2 > 1)) failed: Predicate (2 > 1) did not fail.

for (i <- 0 to 10)
  b = i //error: compile-time refinement only works with literals

更新

在与 scala refined 的创建者交换电子邮件后,这可能会在库本身中得到解决。我在 GitHub here 上打开了一个功能请求问题。如果以及何时使用此功能更新库,我将更新此问题。

您可以使用Refined.unsafeApply跳过编译时检查。如果你想检查不安全的调用是 运行 次,你将不得不手动进行。例如,使用隐式 class:

type ZeroOrOneLiteral = Int Refined Interval.Closed[W.`0`.T, W.`1`.T]

implicit class castableZeroOrOneLiteral(v: Int) {
  def cast: ZeroOrOneLiteral = {
    require(v == 0 || v == 1, "0 or 1 accepted only")
    Refined.unsafeApply(v)
  }
}

var b: ZeroOrOneLiteral = 1

for (i <- 0 to 10)
  b = i.cast

如果还有人关注,我有一个答案。是的,这是可能的。 我最近为 singleton-ops library 做出了贡献,并创建了 TwoFaceChecked 类型来精确地执行此操作。

TwoFace.XXX[T],其中XXX可以是IntString等,是一个值class同时拥有T ] 和 运行-时间值。如果在编译时类型已知,则 T 将拥有文字类型并可用于编译时操作和检查。如果该值不是编译时文字,则 T 将回退到其扩展类型(例如 scala.Int),并且 运行-时间值 class将被使用。