I wish to limit a variable to a boolean integer representation (0 or 1), as an input to a definition. This can be implemented in two ways I've seen so far, one at runtime and one at compile time for literals only.
Is it possible to somehow combine the two, so I can create a type that will reject out of range literal values at compile-time, but will also allow for non literal inputs to be checked at runtime?
Similar to this blog 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
By using scala refined library 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
After an exchange of e-mails with the creator of scala refined, this might get resolved in the library itself. I opened a feature request issue on GitHub here. I'll update this question if and when the library will be updated with this feature.
If anyone still follows, I have an answer. Yes, it's possible.
I recently contributed to the singleton-ops library and created TwoFace
and Checked
types that do precisely this.
TwoFace.XXX[T]
, where XXX
can be Int
, String
, etc., is a value class that possesses both type T
and run-time value. If at compile-time the type is known, then a T
will possess the literal type and can be used for compile-time operations and checks. If the value is not a compile-time literal, then T
will fallback to its widened type (e.g, scala.Int
), and and run-time value of the class will be used instead.