2010-10-14 2 views
8

Я пытаюсь определить общее кольцо классов вычетов в Scala. Кольцо классов остатков определяется некоторым базовым кольцом (например, целыми числами) и модулем (например, два), которое является значением от базового кольца. Оба кольца и их элементы являются объектами, поэтому тип модуля обычно будет зависимым типом, в зависимости от базового кольца. Я понимаю, что это не разрешено в 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); 
} 

ответ

3

Это похоже на работу, но я не мог избавиться от броска при расчете представитель:

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) 
} 

ОТВЕТ: Будьте осторожны с чертой применения, это по праву устарело.

3

UPDATE: Добавлена ​​IntRing для уточнения изменений в trait Ring

Проблема, кажется, что тип inferencer не автоматически выберет наиболее конкретный тип, который что вам нужно в вашем случае. В дополнение к этому у вас не может быть аргумент зависимого типа в том же списке параметров, что и определяющий тип.

Что вы могли бы сделать, это вытащить экземпляр, тип зависит от того, во внешнем объеме (что делается в 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 = [email protected] 
+0

Я по-прежнему получаю аналогичную ошибку при попытке создать экземпляр кольца с помощью 'new Integers.rings.ResidueClassRing (two)'. – starblue

+0

Я обновил код с помощью 'IntRing', чтобы сделать его более понятным. У меня также была проблема, когда я использовал псевдонимы типов, как вы, - не знаю почему. – Moritz

+0

Моя ошибка: в моем определении целых чисел у меня все еще была 'E' вместо' Element'. – starblue