我试图围绕无符号整数和有符号整数设计编号系统。这两种类型都有underlying
值,表示Scala编号系统中的数字。这是我到目前为止的类型层次结构。
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
}
我想在特征underlying
中定义Number
,因此编译器可以强制在所有子节点中定义underlying
。但是,underlying
的类型因每个特征而异 - 我希望为每种类型保留尽可能小的类型。例如,UInt32
可以在Scala中存储为long
,而UInt64
则需要存储为BigInt
。
最有效的方法是什么?
答案 0 :(得分:3)
您可以在父特征中声明type
并在子特征中覆盖它。
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
}
一个示例,只是为了表明在不清楚的情况下使用路径依赖类型:
def getUnderlying(x: Number): x.A = x.underlying
为了使返回类型正确,我认为可能需要另一个type
。
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)
}
答案 1 :(得分:0)
最有效的方法是将原始数字(Int Double ...)存储为原始类型。
无符号应存储在Type参数中,该参数将在运行时删除。当你让简单的case类扩展AnyVal时,Scala会这样做。
以下代码为Ints,Longs,Doubles和Bigint执行此操作。 我在adition中添加了一些分类给unsigned,并将unsigned重命名为positive。
此外,由于分类都是在类型系统中完成的,因此我们不需要提供尽可能多的重载+ - 和*函数。当尝试为所有数字类型实现此功能时,这将节省空间。
在各种类型之间架起桥梁时还有一些工作要做。我稍后会看一下。
分类特征:
sealed trait SignTag{
type SubTag <:SignTag;
type AddTag <:SignTag;
type MultTag<:SignTag;
}
sealed trait Signed extends SignTag{
type SubTag=Signed;
type AddTag=Signed;
type MultTag=Signed;
}
sealed trait Positive extends SignTag{
type SubTag=Signed;
type AddTag=Negative;
type MultTag=Negative;
}
sealed trait Negative extends SignTag{
type SubTag=Signed;
type AddTag=Negative;
type MultTag=Positive;
}
sealed trait Zero extends SignTag{
type SubTag=Zero;
type AddTag=Zero;
type MultTag=Zero;
}
Int wrapper:
object SInt {
@inline
implicit def toSigned[T <: SignTag](int:SInt[T]):SInt[Signed]=int.asInstanceOf[SInt[Signed]];
@inline implicit def toLong[T <: SignTag](int:SInt[T]):SLong[T]=SLong(int.underlying);
@inline implicit def toDouble[T <: SignTag](int:SInt[T]):SDouble[T]=SDouble(int.underlying);
@inline implicit def toBig[T <: SignTag](int:SInt[T]):SBigInt[T]=SBigInt(int.underlying);
}
case class SInt[T <: SignTag](val underlying:Int) extends AnyVal{
def -(second: SInt[_ <: T#InTag]):SInt[T#SubTag]=new SInt[T#SubTag](underlying - second.underlying);
def +(second: SInt[_ <: T#InTag]):SInt[T#AddTag]=new SInt[T#AddTag](underlying + second.underlying);
def *(second: SInt[_ <: T#InTag]):SInt[T#MultTag]=new SInt[T#MultTag](underlying * second.underlying);
def assertSameType(other:SInt[T])={};
}
长包装器:
object SLong {
@inline
implicit def toSigned[T <: SignTag](int:SLong[T]):SLong[Signed]=int.asInstanceOf[SLong[Signed]];
@inline implicit def toDouble[T <: SignTag](int:SLong[T]):SDouble[T]=SDouble(int.underlying);
@inline implicit def toBig[T <: SignTag](int:SLong[T]):SBigInt[T]=SBigInt(int.underlying);
}
case class SLong[T <: SignTag](val underlying:Long) extends AnyVal{
def -(second: SLong[_ <: T#InTag]):SLong[T#SubTag]=new SLong[T#SubTag](underlying - second.underlying);
def +(second: SLong[_ <: T#InTag]):SLong[T#AddTag]=new SLong[T#AddTag](underlying + second.underlying);
def *(second: SLong[_ <: T#InTag]):SLong[T#MultTag]=new SLong[T#MultTag](underlying * second.underlying);
def assertSameType(other:SLong[T])={};
}
Double wrapper:
object SDouble {
@inline
implicit def toSigned[T <: SignTag](int:SDouble[T]):SDouble[Signed]=int.asInstanceOf[SDouble[Signed]];
}
case class SDouble[T <: SignTag](val underlying:Double) extends AnyVal{
def -(second: SDouble[_ <: T#InTag]):SDouble[T#SubTag]=new SDouble[T#SubTag](underlying - second.underlying);
def +(second: SDouble[_ <: T#InTag]):SDouble[T#AddTag]=new SDouble[T#AddTag](underlying + second.underlying);
def *(second: SDouble[_ <: T#InTag]):SDouble[T#MultTag]=new SDouble[T#MultTag](underlying * second.underlying);
def assertSameType(other:SDouble[T])={};
}
BigInt包装器:
object SBigInt {
@inline
implicit def toSigned[T <: SignTag](int:SLong[T]):SLong[Signed]=int.asInstanceOf[SLong[Signed]];
@inline
implicit def toDouble[T <: SignTag](int:SBigInt[T]):SDouble[T]=SDouble(int.underlying.toDouble);
}
case class SBigInt[T <: SignTag](val underlying:BigInt) extends AnyVal{
def -(second: SBigInt[_ <: T#InTag]):SBigInt[T#SubTag]=new SBigInt[T#SubTag](underlying - second.underlying);
def +(second: SBigInt[_ <: T#InTag]):SBigInt[T#AddTag]=new SBigInt[T#AddTag](underlying + second.underlying);
def *(second: SBigInt[_ <: T#InTag]):SBigInt[T#MultTag]=new SBigInt[T#MultTag](underlying * second.underlying);
def assertSameType(other:SBigInt[T])={};
}
测试语法:
class CompileToTest {
val signed=new SInt[Signed](5);
val positive=new SInt[Positive](5);
val negative=new SInt[Negative](-5);
val zero=new SInt[Zero](0);
(signed + signed).assertSameType(signed);
(negative + signed).assertSameType(signed);
(positive - positive).assertSameType(signed);
(positive * negative).assertSameType(signed);
(zero + zero).assertSameType(zero);
val positiveDouble=SDouble[Positive](4.4)
val negativeDouble=SDouble[Negative](-4.4)
val signedDouble=SDouble[Signed](-4.4)
(positiveDouble * negativeDouble).assertSameType(signedDouble);
}
聚苯乙烯。实际上没有看过字节码,但文档认为这应该内联并编译成基元。
我喜欢这种语言。