Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a "n to m" restricted int variant in Scala?

Tags:

scala

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 )
like image 617
akauppi Avatar asked Sep 04 '12 01:09

akauppi


2 Answers

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.

like image 112
paradigmatic Avatar answered Oct 30 '22 07:10

paradigmatic


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.

like image 38
Kristian Domagala Avatar answered Oct 30 '22 08:10

Kristian Domagala