質問の声明Scalaの型投影を専門にする方法は?
は抽象型メンバーA
が含まれているタイプT
考えてみましょう:
trait T {
type A
}
私は型パラメータとしてT0 <: T
かかりますが、専門のクラスを作成したいのですがタイプ投影T0#A
にあります。たとえば、以下では、foo
メソッドを特殊化することはできますか?
class Foo[T0 <: T] {
def foo(a: T0#A, f: T0#A => T0#A) = f(a)
}
@specialized
とT0
に注釈を付けることは、所望の結果を達成しないことに注意してください。タイププロジェクションにfoo
を特化する良い方法はありますか?
限られたソリューションは:専門の親クラスSpecializedFoo
から継承して
trait SpecializedFoo[@specialized A0, T0 <: T] {
def foo(a: A0, f: A0 => A0) = f(a)
}
class Foo2[T0 <: T] extends SpecializedFoo[T0#A, T0]
:この特定のケースでは追加のパラメータ
持つ専門の親クラスから継承し、ここでT0#A
に特化する方法ですFoo2.foo
が専門であることを保証します。
専門
の検証はFoo2.foo
ではなくFoo.foo
は、特化され、我々はT#A
プリミティブダブル、
trait ExplicitT extends T {
type A = Double
}
object Test {
def test1 = (new Foo[ExplicitT]).foo(1.0, _ + 1.0)
def test2 = (new Foo2[ExplicitT]).foo(1.0, _ + 1.0)
}
で明示的T
でそれらを呼び出すことができるというバイトコードを確認するには、することができます":javap -v Test"コマンドでREPLから調べてください。
public double test1();
Code:
Stack=4, Locals=1, Args_size=1
0: new #16; //class Foo
3: dup
4: invokespecial #18; //Method Foo."<init>":()V
7: dconst_1
8: invokestatic #24; //Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;
11: new #26; //class Test$$anonfun$test1$1
14: dup
15: invokespecial #27; //Method Test$$anonfun$test1$1."<init>":()V
18: invokevirtual #31; //Method Foo.foo:(Ljava/lang/Object;Lscala/Function1;)Ljava/lang/Object;
21: invokestatic #35; //Method scala/runtime/BoxesRunTime.unboxToDouble:(Ljava/lang/Object;)D
24: dreturn
LineNumberTable:
line 13: 0
public double test2();
Code:
Stack=5, Locals=1, Args_size=1
0: new #38; //class Foo2
3: dup
4: invokespecial #39; //Method Foo2."<init>":()V
7: dconst_1
8: new #41; //class Test$$anonfun$test2$1
11: dup
12: invokespecial #42; //Method Test$$anonfun$test2$1."<init>":()V
15: invokeinterface #48, 4; //InterfaceMethod SpecializedFoo.foo$mcD$sp:(DLscala/Function1;)D
20: dreturn
LineNumberTable:
line 14: 0
ボクシングはtest1
に表示されますが、test2
には表示されません。
制限
編集7/9上記のトリックは、私が最初に実現よりも制限されています。これは、このケースを専門にするために、まったく動作しません。
trait T {
type A
def x: A
def f: A => Double
}
class Foo[T0 <: T] {
def foo(t: T0) = t.f(t.x)
}
は、私が(架空の)コンパイラは、原則としてにA
に特化できない理由を見ません。コンパイル時に特定のT#A
が分かっている場合にのみ、特殊化されたバージョンが使用可能になります。自然な実用的な解決方法はA
をタイプパラメータT
に持ち上げることですが、私がそれを避けることができるかどうか疑問に思っていました。
私は、実装する 'クラスバー拡張Foo [S]'で、 'S <:T'がコンパイル時に明示的に認識され、' S#A'がプリミティブである場合、特殊なメソッドが必要です。上記のトリック(特別な型パラメータを持つ 'SpecializedFoo'親クラス)を使用すると、私はボクシングを排除することができたため、これが機能することが分かりました。これは、Scala REPLの" javap -v "で検証しました。質問のサンプルコードは、もちろん簡素化されています...多分詳細を入れるべきかもしれません。 –
私は、ボクシングがどのように現れるか、そしてそれが専門化によってどのように取り除かれるかについて、より明確になるように質問を更新しました。 –