Как эмулировать зависимый тип в Scala - PullRequest
8 голосов
/ 14 октября 2010

Я пытаюсь определить общее кольцо класса остатка в 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 для Гелиоса. Проблема уже возникла для 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);
}

Ответы [ 2 ]

3 голосов
/ 14 октября 2010

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

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

Кстати: будьте осторожны с чертой Application, она по праву считается устаревшей.

3 голосов
/ 14 октября 2010

ОБНОВЛЕНИЕ: Добавлено 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...