如何模拟Scala中的依赖类型
-
30-09-2019 - |
题
我正在尝试在Scala中定义一个通用的残留类环。残基类环由某些基数环(例如整数)和模量(例如两个)定义,这是从基数中的值。戒指及其元素都是对象,因此,模量的类型通常是依赖类型,具体取决于基数环。我知道在Scala中不允许这样做(有充分的原因),因此我试图通过近似类型并在构造残基类环时进行运行时检查。
定义 ResidueClassRing
但是,没有错误接受,Scala不允许我实例化,以进行参数 two
我收到错误消息
type mismatch;
found : dependenttypetest.DependentTypeTest.two.type
(with underlying type dependenttypetest.Integers.Integer)
required: dependenttypetest.EuclideanRing#E
难道我做错了什么?这可能是Scala类型检查器中的错误吗?有更好的定义方法 ResidueClassRing
?
这是用于Helios的Eclipse IDE中的Scala 2.8.0。问题已经发生在2.7.x。这是代码的简化版本:
package dependenttypetest
class EuclideanRing
{
thisRing =>
type E <: EuclideanRingElement;
def one: E;
trait EuclideanRingElement
{
def ring = thisRing;
def +(b: E): E;
def %(b: E): E;
}
}
object Integers extends EuclideanRing
{
type E = Integer;
val one: Integer = new Integer(1);
class Integer(n: Int) extends EuclideanRingElement
{
val intValue: Int = n;
def +(b: Integer): Integer = new Integer(intValue + b.intValue);
def %(b: Integer): Integer = new Integer(intValue % b.intValue);
}
}
class ResidueClassRing (val baseRing : EuclideanRing, m : EuclideanRing#E)
{
val modulus: baseRing.E =
m match {
case e: baseRing.E if m.ring == baseRing => e;
case _ => throw new IllegalArgumentException("modulus not from base ring");
};
type E = ResidueClassRingElement;
def one: E = new ResidueClassRingElement(baseRing.one);
class ResidueClassRingElement (e : baseRing.E)
{
def representative: baseRing.E = e % modulus;
def +(b: E) = new ResidueClassRingElement(
this.representative + b.representative);
}
}
object DependentTypeTest extends Application
{
val two = new Integers.Integer(2);
val mod2ring = new ResidueClassRing(Integers, two);
println(mod2ring.one + mod2ring.one);
}
解决方案
这似乎有效,但是在计算代表时,我无法摆脱演员阵容:
package dependenttypetest
abstract class EuclideanRing{
thisRing =>
type E <: EuclideanRingElement;
def one: E;
trait EuclideanRingElement
{
def ring = thisRing;
def +(b: E): E;
def %(b: E): E;
}
}
class Integers extends EuclideanRing {
type E = Integer;
val one: Integer = new Integer(1);
class Integer(n: Int) extends EuclideanRingElement
{
val intValue: Int = n;
def +(b: Integer): Integer = new Integer(intValue + b.intValue);
def %(b: Integer): Integer = new Integer(intValue % b.intValue);
override def toString = "Int" + intValue
}
}
object Integers extends Integers
class ResidueClassRing[ER <: EuclideanRing] (modulus : ER#E) {
val baseRing = modulus.ring
type E = ResidueClassRingElement;
def one: E = new ResidueClassRingElement(baseRing.one);
class ResidueClassRingElement (e : baseRing.E)
{
def representative = e % modulus.asInstanceOf[baseRing.E];
def +(b: E) = new ResidueClassRingElement(
this.representative + b.representative);
override def toString = "RC(" + representative + ")"
}
}
object DependentTypeTest extends Application {
val two = new Integers.Integer(2);
val mod2ring = new ResidueClassRing[Integers](two)
println(mod2ring.one + mod2ring.one)
}
顺便说一句:要小心应用特征,它是理所当然的。
其他提示
更新: 添加 IntRing
澄清更改 trait Ring
问题似乎是类型推论器并未自动选择最特定的类型,这是您所需的。除此之外,您不能在与定义类型的同一参数列表中具有依赖类型参数。
您可以做的就是拉动类型在外部范围中取决于的实例(这是在 Rings
班级),并在实例化时迫使编译器选择最特定的类型 Rings
班级:
trait Ring {
type Element <: EuclideanRingElement
def one: Element
// for convenience could be defined anywhere of course
lazy val rings: Rings[this.type] = new Rings[this.type](this)
trait EuclideanRingElement {
def +(e: Element): Element
def %(e: Element): Element
}
}
class Rings[R <: Ring](val base: R) {
class ResidueClassRing(m: base.Element) {
def one = new Element(base.one)
class Element(e: base.Element) {
def repr = e % m
def +(that: Element) = new Element(this.repr + that.repr)
}
}
}
object IntRing extends Ring {
val one = new Element(1)
class Element(val n: Int) extends EuclideanRingElement {
def +(that: Element) = new Element(this.n + that.n)
def %(that: Element) = new Element(this.n % that.n)
override def toString = n formatted "Int(%d)"
}
}
现在您可以这样使用:
scala> import IntRing._
import IntRing._
scala> val two = new Element(2)
two: IntRing.Element = Int(2)
scala> val r2 = new rings.ResidueClassRing(two)
r2: IntRing.rings.ResidueClassRing = Rings$ResidueClassRing@4b5075f9
不隶属于 StackOverflow