2011-08-08 11 views
33

私はクラス階層を設計しています。クラス階層は、いくつかの特性とともに基本クラスで構成されています。基本クラスはいくつかのメソッドのデフォルト実装を提供し、形質はabstract overrideによって特定のメソッドを選択的にオーバーライドし、スタック可能なtraits/mixinsとして機能します。今、設計の観点から形質を暗黙的な "コンストラクタパラメータ"として宣言するには?

これがうまく機能、およびドメインにマップ私はここから述語(別の形質)とここからフィルタリング機能(1つの形質)を追加できるように、など

にしかし、私は、暗黙的なパラメータを取るために私の特性のいくつかが好きです。私はこれがまだデザインの観点からは意味があり、実際には紛らわしいとは思わないことがうれしいです。しかし、私はそれを実行するコンパイラを説得することはできません。

この問題の中核は、形質にコンストラクタ引数を指定できないため、暗黙的にマークすることができるようです。メソッド実装内の暗黙的なパラメータの参照は、予期した "暗黙の値を見つけることができませんでした"というメッセージでコンパイルできません。私は

implicit val e = implicitly[ClassName] 

経由でメソッド内で利用可能に(実際には、それがスコープ内に常にあります、)建設段階から暗黙的に「伝播」しようとしたが、定義することを(は間違いないとして、あなたの多くが期待して)同じメッセージで失敗しました。

ここでの問題は、コンパイラにimplicit ClassNameフラグを付けると、呼び出し元(つまり、特性をオブジェクトに混ぜたもの)が暗黙的に。現在私の呼び出し元ですが、コンパイラはこのレベルでチェックしていません。


特定の暗黙のは、建設時に利用できるように必要なものとして形質をマークする方法はありますか?

(そうでない場合、これは単にまだ実装されていないか、これは非現実的である理由深い理由があるのですか?)

答えて

15

実は、私は前にかなり頻繁にこのことを思っていたが、ちょうどこのアイデアを思い付きました。あなたはこのようにそれを行うことができ

trait T { 
    // no need to ever use it outside T 
    protected case class ClassNameW(implicit val wrapped: ClassName) 

    // normally defined by caller as val implWrap = ClassNameW 
    protected val implWrap: ClassNameW 

    // will have to repeat this when you extend T and need access to the implicit 
    import implWrap.wrapped 

    def foo = ... // using wrapped here 
} 
+0

これは、匿名オブジェクトで明示的に 'implWrap'を定義することはありません。なぜなら、それは特性の抽象フィールドなのですから? (もしそうでなければ、私はそれがどのように設定されているのか理解できません;あなたは気になりますか?) –

+0

はい、コメントを見てください:暗黙的に使用したい場合、 'val implWrap = ClassNameW'と書くことができます。私はそれを行うより良い方法はありません:あなたが質問で言及しているように、形質は暗黙的にマーキングできるコンストラクタパラメータを持っていません。 –

+7

もちろん、私はもっと良い解決策を見てとてもうれしいです。 –

0

:あなたは

trait T(implicit impl: ClassName) { 
    def foo = ... // using impl here 
} 

[オリジナル版は、他の方法のための暗黙のへのアクセスを提供していませんでしたEDITED]:までを翻訳することができ

abstract class C 

trait A { this: C => 
    val i: Int 
}  

implicit val n = 3 

val a = new C with A { 
    val i = implicitly[Int] 
} 

しかし、私その中にポイントがあるかどうかわからない - 暗黙の値を明示的に参照することもできます。

インスタンス化でiの実装を取り除くことが望ましいと思いますが、あなた自身が言うように、問題の核心は、形質が暗黙的であろうとなかろうと、関係ない。

この問題の可能な解決策がすでに有効な構文に新しい機能を追加することです:暗黙的な範囲にあった場合iは、コンパイラによって実装されるだろう

trait A { 
    implicit val i: Int 
} 

11

私はこの問題に数回遭遇しましたが、実際には少し面倒ですが、それほど多くはありません。抽象メンバとパラメータは、同じ長所と短所を持って同じことを行う2つの代替方法です。 *

したがって、実装クラスが暗黙的に提供する必要があるように、その特性に抽象値宣言を付けるだけでよいのですが、抽象メンバを持つ特性はそれほど不便ではありません。あなたのために。次の例を参照してください - 正しくコンパイル、および特定の形質を実装するための2つの方法を示している:

trait Base[T] { 
    val numT: Ordering[T] 
} 
/* Here we use a context bound, thus cannot specify the name of the implicit 
* and must define the field explicitly. 
*/ 
class Der1[T: Ordering] extends Base[T] { 
    val numT = implicitly[Ordering[T]] 
    //Type inference cannot figure out the type parameter of implicitly in the previous line 
} 
/* Here we specify an implicit parameter, but add val, so that it automatically 
* implements the abstract value of the superclass. 
*/ 
class Der2[T](implicit val numT: Ordering[T]) extends Base[T] 

私が示す基本的な考え方は、また、クヌートアルネVedaaの答えに存在しているが、私は、より魅力的で便利に作ってみましたたとえば、不要な機能の使用を停止します。

*これは、特性がパラメータを受け入れることができない理由ではありません。わかりません。私はこの制限が受け入れられると主張しています。

+2

しかし、この方法では、 'Base [T]'でメソッドを定義している間に暗黙の 'Ordering [T]'にアクセスすることはできません。 'numT'を暗黙的にすると、この問題は解決されますが、' val numT =暗黙的に[Ordering [T]] 'は無限ループになります。 –

+1

この問題に対処するために 'Base'を変更した場合、Der1を書くことはできませんが、Der2はとにかく動作します。 'trait Base [T] { 暗黙の値numT:順序[T] } クラスDer2 [T](暗黙の値numT:順序[T])は、ベース[T]' – Blaisorblade

0

これは不可能なように、暗黙的にvalを基底クラスのコンストラクタで宣言するオプションに行きました。指摘されているように、これは理想的ではありませんが、コンパイラを満たし、実際には私の特別な場合にはあまり負担になりません。

誰かがもっと良い解決策を持っていれば、私はそれを聞いて受け入れていただければ幸いです。

7

これはできません。

しかし、implicitlyとScalaの型推論を使用して、これをできるだけ無痛にすることができます。

trait MyTrait { 

    protected[this] implicit def e: ClassName 

} 

、その後

class MyClass extends MyTrait { 

    protected[this] val e = implicitly // or def 

} 

簡潔、さらには拡張クラスのタイプを書く必要はありません。

関連する問題