Question

Je suis en train de définir un anneau de classe générique reste dans Scala. Un anneau de classe résidu est définie par une bague de base (par exemple, les nombres entiers de) et un module (par exemple deux), qui est une valeur à partir de l'anneau de base. Les deux bagues et les éléments sont des objets, par conséquent, le type du module serait normalement un type dépendant, en fonction de l'anneau de base. Je comprends que ce n'est pas autorisé à Scala (pour de bonnes raisons), donc je suis en train de l'imiter en se rapprochant du type et de faire un contrôle d'exécution lorsque l'anneau de classe résidu est construit.

La définition de ResidueClassRing est acceptée sans erreur, cependant, Scala ne laisse pas me instancier, pour le two argument que je reçois le message d'erreur

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

Ai-je fait quelque chose de mal? Serait-ce un bug dans le type Scala vérificateur? Y at-il une meilleure façon de définir ResidueClassRing?

est avec Scala 2.8.0 dans l'IDE Eclipse Helios. Le problème déjà eu lieu pour 2.7.x. Voici une version simplifiée du code:

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);
}
Était-ce utile?

La solution

Cela semble fonctionner, mais je ne pouvais pas se débarrasser de la distribution lors du calcul représentant:

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:. Soyez prudent avec le trait d'application, il est à juste titre dépréciée

Autres conseils

Mise à jour: Ajout IntRing pour clarifier les changements dans trait Ring

Le problème semble être que le type inferencer ne capte pas automatiquement le type le plus spécifique qui est ce que vous avez besoin dans votre cas. En plus de cela, vous ne pouvez pas avoir un argument de type dépendant de la même liste de paramètres comme le type de définition.

Qu'est-ce que vous pourriez faire est de tirer l'instance que le type dépend du périmètre extérieur (ce qui se fait dans la classe Rings) et pour forcer le compilateur de choisir le type le plus spécifique lors de l'instanciation de la classe 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)"
  }
}

Maintenant, vous pouvez l'utiliser comme ceci:

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top