Как эмулировать зависимый тип в 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
?
Это с Scala 2.8.0 в Eclipse IDE для Helios.Проблема уже возникла для 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
class) и заставить компилятор выбирать наиболее конкретный тип при создании экземпляра 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