كيفية محاكاة نوع تابع في سكالا
-
30-09-2019 - |
سؤال
أحاول تحديد حلقة فئة بقايا عامة في سكالا. يتم تعريف حلقة فئة البقايا بواسطة بعض الحلقة الأساسية (على سبيل المثال الأعداد الصحيحة) ومعامل (مثل اثنين) ، وهي قيمة من الحلقة الأساسية. كل من الحلقات وعناصرها هي كائنات ، وبالتالي فإن نوع المعامل عادة ما يكون نوعًا تابعًا ، اعتمادًا على الحلقة الأساسية. أنا أفهم أن هذا غير مسموح به في 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
الطبقة) وإجبار المترجم على اختيار النوع الأكثر تحديداً عند إنشاء إنشاء 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