2011-07-08 13 views
9

質問の声明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) 
} 

@specializedT0に注釈を付けることは、所望の結果を達成しないことに注意してください。タイププロジェクションに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に持ち上げることですが、私がそれを避けることができるかどうか疑問に思っていました。

答えて

1

これはコンパイラの制限です。 1 できませんは一般的にタイプパラメータの要素に特化しています。しかし、提案されたトリックは私の目的のために十分である:それは、形質GenOpsに特別な実装を継承するため、形質Opsは特殊ます

trait Types { 
    type A 
    type B 
} 

trait GenOps[@specialized A, @specialized B] { 
    ... 
} 

trait Ops[T <: Types] extends GenOps[T#A, T#B] 

この方法です。私の動機は、OpsがとT#Bの両方ではなく、単一のタイプのパラメータTを取るようにしたいということです。Opsも、より高い種類のタイプ(パラメータとしてTが必要な場合に必要です)。

1

どのように動作するかわかりません。クラスをコンパイルするときに特殊化が行われ、そのときはAは不明です。

+0

私は、実装する 'クラスバー拡張Foo [S]'で、 'S <:T'がコンパイル時に明示的に認識され、' S#A'がプリミティブである場合、特殊なメソッドが必要です。上記のトリック(特別な型パラメータを持つ 'SpecializedFoo'親クラス)を使用すると、私はボクシングを排除することができたため、これが機能することが分かりました。これは、Scala REPLの" javap -v "で検証しました。質問のサンプルコードは、もちろん簡素化されています...多分詳細を入れるべきかもしれません。 –

+0

私は、ボクシングがどのように現れるか、そしてそれが専門化によってどのように取り除かれるかについて、より明確になるように質問を更新しました。 –

関連する問題