Scalaの依存型をエミュレートする方法
-
30-09-2019 - |
質問
Scalaで一般的な残基のクラスリングを定義しようとしています。残留クラスリングは、ベースリング(整数など)とモジュラス(2つ)によって定義されます。これは、ベースリングの値です。リングとその要素の両方がオブジェクトであるため、通常、弾性率のタイプはベースリングに応じて依存型になります。これはScalaでは許可されていないことを理解しているので(正当な理由で)、タイプを近似し、残留クラスリングが構築されたときにランタイムチェックを行うことでエミュレートしようとしています。
の定義 ResidueClassRing
エラーなしで受け入れられていますが、Scalaは議論のために私にそれを瞬時にさせません two
エラーメッセージが表示されます
type mismatch;
found : dependenttypetest.DependentTypeTest.two.type
(with underlying type dependenttypetest.Integers.Integer)
required: dependenttypetest.EuclideanRing#E
私は何か間違ったことをしていますか?これはSCALAタイプチェッカーのバグでしょうか?定義するより良い方法はありますか? ResidueClassRing
?
これは、ヘリオスのEclipse IDEのScala 2.8.0を使用しています。問題はすでに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