I'd like to have types s.a. Int_1to3
or Uint
in Scala. Preferably, there'd be a general factory method that can provide any such.
It's mostly for self-documentary purposes, but also the values would be checked on arrival (i.e. via 'assert').
I was somewhat surprised not to have found a solution to this during my initial (google) search. The closest I came to is Unsigned.scala, but that's overkill for my needs.
This must be dead simple?
Just to give an idea on the usage, something like this would be splendid! :)
type Int_1to3= Int_limited( 1 to 3 )
type Uint= Int_limited( _ >= 0 )
I see two potential solutions:
First you can have a look at Unboxed Type Tags. They allow to attach a type a compile time without having to box the integer. The compiler will enforce that they are used when needed, but values are checked at runtime.
From the cited article, you could write something like:
type Tagged[U] = { type Tag = U }
type @@[T, U] = T with Tagged[U]
trait Positive
trait One2Three
type PositiveInt = Int @@ Positive
type SmallInt = Int @@ One2Three
//Builds a small int
def small(i: Int): SmallInt = {
require( i > 0 && i < 4, "should be between 1 and 3" )
i.asInstanceOf[SmallInt]
}
//Builds a positive int
def positive( i: Int): PositiveInt = {
require( i >= 0, "should be positive" )
i.asInstanceOf[PositiveInt]
}
//Example usage in methods
def mul( i: SmallInt, j: PositiveInt ): PositiveInt = positive(i*j)
Then in the REPL:
scala> mul( small(2), positive(4) )
res1: PositiveInt = 8
scala> mul( small(4), positive(2) ) //RUNTIME ERROR
java.lang.IllegalArgumentException: should be between 1 and 3
scala> mul( 2, positive(2) ) //COMPILE TIME ERROR
<console>:16: error: type mismatch;
found : Int(2)
required: SmallInt
mul( 2, positive(2) )
^
The second solutions may be value classes proposed for Scala 2.10. You can read the SIP-15 to see how to use them.
A "pattern" you can use here is to declare a sealed type with a private constructor as a wrapper around the underlying value, which is restricted to a single point that it can be validated and instantiated. Eg
sealed abstract class Int_1to3(val i:Int)
object Int_1to3 {
def apply(i:Int):Option[Int_1to3] =
if (1.to(3).contains(i)) Some(new Int_1to3(i){}) else None
}
That way, whenever you end up with an instance of some x
of type Int_1to3
, you have a compile-time guarantee that x.i
will be 1
, 2
or 3
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With