I'm trying to design a numbering system around both unsigned integers and signed integers. Both of these types have an underlying
value that represents the number in Scala's number system. Here is the type hierarchy I have so far.
sealed trait Number {
def + (num : Number) : Number = ???
def - (num : Number) : Number = ???
def * (num : Number) : Number = ???
}
sealed trait SignedNumber extends Number
sealed trait UnsignedNumber extends Number
sealed trait UInt32 extends UnsignedNumber {
def underlying : Long
}
sealed trait UInt64 extends UnsignedNumber {
def underlying : BigInt
}
sealed trait Int32 extends SignedNumber {
def underlying : Int
}
sealed trait Int64 extends SignedNumber {
def underlying : Long
}
I would like to define underlying
in the trait Number
so the compiler can enforce that underlying
is defined in all of the children. However, the types for underlying
vary for each trait - I want to keep the smallest possible type for each type. For instance, a UInt32
can be stored as a long
in Scala, while a UInt64
needs to be stored as a BigInt
.
What is the most effective way to do this?
Both can store 256 different values, but signed integers use half of their range for negative numbers, whereas unsigned integers can store positive numbers that are twice as large.
A signed integer is a 32-bit datum that encodes an integer in the range [-2147483648 to 2147483647]. An unsigned integer is a 32-bit datum that encodes a nonnegative integer in the range [0 to 4294967295].
Signed and unsigned are those two ways. Since every integer can be identified using it's sign such as positive (+) or negative (-). On the other hand in an unsigned integer can not store negative values, only positive values can be stored in the unsigned variable. Example of signed integer: Sign int a = -4.
Signed numbers use sign flag or can be distinguish between negative values and positive values. Whereas unsigned numbers stored only positive numbers but not negative numbers.
You can declare a type
in the parent trait and override it in the subtraits.
sealed trait Number {
type A
def underlying: A
def + (num : Number) : Number = ???
def - (num : Number) : Number = ???
def * (num : Number) : Number = ???
}
sealed trait SignedNumber extends Number
sealed trait UnsignedNumber extends Number
sealed trait UInt32 extends UnsignedNumber {
override type A = Long
}
sealed trait UInt64 extends UnsignedNumber {
override type A = BigInt
}
sealed trait Int32 extends SignedNumber {
override type A = Int
}
sealed trait Int64 extends SignedNumber {
override type A = Long
}
An example just to show use of the path-dependent type in case that isn't clear:
def getUnderlying(x: Number): x.A = x.underlying
To get the return types correct, I think another type
might be required.
sealed trait Number {
type A
type B
def underlying: A
def +(that: B): B
}
sealed trait UInt32 extends Number { x =>
override type A = Long
override type B = UInt32
override def +(y: B): B = new UInt32 {
// todo - naive implementation, doesn't check overflow
override val underlying = x.underlying + y.underlying
}
}
def main(args: Array[String]) {
print((
new UInt32 { def underlying = 3 } +
new UInt32 { def underlying = 4 }
).underlying)
}
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