Frage

Ich versuche, einen generischen Restklassenring in Scala zu definieren. Ein Restklassenring wird durch einen Basisring (z die ganzen Zahlen) und ein Modul (beispielsweise zwei), definiert, der ein Wert aus dem Basisring ist. Beiden Ringe und ihre Elemente sind Objekte, also die Art des Moduls würde normalerweise ein abhängiges Typ sein, abhängig von dem Basisring. Ich verstehe dies in Scala nicht erlaubt (aus guten Gründen), so versuche ich, es zu emulieren, indem die Art annähert und eine Laufzeitprüfung zu tun, wenn der Restklassenring aufgebaut ist.

Die Definition von ResidueClassRing ohne Fehler akzeptiert wird, jedoch nicht Scala nicht lassen Sie mich es instanziiert, für das Argument two erhalte ich die Fehlermeldung

type mismatch;
found   : dependenttypetest.DependentTypeTest.two.type 
(with underlying type dependenttypetest.Integers.Integer)  
required: dependenttypetest.EuclideanRing#E

Bin ich etwas falsch? Könnte dies ein Fehler in der Scala Typprüfer sein? Gibt es eine bessere Art und Weise ResidueClassRing zu definieren?

Dies ist mit Scala 2.8.0 in dem Eclipse-IDE für Helios. Das Problem trat bereits für 2.7.x. Hier ist eine vereinfachte Version des Codes:

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);
}
War es hilfreich?

Lösung

Das scheint zu funktionieren, aber ich konnte nicht von den Darstellern loszuwerden, wenn Vertreter der Berechnung:

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

BTW:. Seien Sie vorsichtig mit der Application-Eigenschaft, ist es zu Recht als veraltet

Andere Tipps

UPDATE: hinzugefügt IntRing zu klären Änderungen in trait Ring

Das Problem scheint zu sein, dass der Typ Rückschließer automatisch die spezifische Art nicht wählen, welche ist das, was Sie in Ihrem Fall benötigen. Zusätzlich zu, dass Sie ein abhängiges Typargument in der gleichen Parameterliste als definiert Typen nicht haben können.

Was könnten Sie tun, ist die Instanz zu ziehen, dass der Typ abhängt, in dem äußeren Umfang (die in der Rings Klasse gemacht wird) und die Compiler zu zwingen, den meisten spezifischen Typen zu wählen, wenn die Rings Klasse instanziieren:

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

Jetzt können Sie es wie folgt verwendet werden:

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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top