Scala精简整数,用于编译时文字和运行时变量

时间:2016-07-09 19:15:19

标签: scala type-safety

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

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

运行时保护

与此博客文章类似: 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的创建者交换电子邮件后,这可能会在图书馆本身得到解决。我在GitHub here上打开了一个功能请求问题。如果库将使用此功能进行更新,我将更新此问题。

2 个答案:

答案 0 :(得分:1)

您可以使用Refined.unsafeApply跳过编译时检查。如果您想检查不安全的呼叫是否是运行时间,则必须手动执行。例如,使用隐式类:

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

答案 1 :(得分:1)

如果还有人跟着,我有答案。是的,这是可能的。 我最近为singleton-ops library做出了贡献,并创建了TwoFaceChecked类型。

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