1

表現タイプを持つトレイトは自己参照であるため、変数がそのトレイトのインスタンスを保持していることを宣言するのは少し難しいです。この例では、変数がトレイトのインスタンスを保持していることを宣言し、関数がそのトレイトのインスタンスを取得して返すことを宣言し、変数を使用してその関数を呼び出します。

trait Foo[+A <: Foo[A]]
case class Bar() extends Foo[Bar]
case class Grill() extends Foo[Grill]

// Store a generic instance of Foo
val b: Foo[_] = if(true) {
  Bar()
} else {
  Grill()
}

// Declare a function that take any Foo and returns a Foo of the same type
// that "in" has in the calling context
def echoFoo[A <: Foo[A]](in: A): A = in

// Call said function
val echo = echoFoo(b)

次のエラーで失敗します。

inferred type arguments [this.Foo[_$1]] do not conform to method 
echoFoo's type parameter bounds [A <: this.Foo[A]]
val echo = echoFoo(b)
           ^

さて、これは(私が完全には理解していない方法で[_])のようなものであるため、理にかなっています。Anyおそらく必要なのはFoo[Foo[_]]、のようなもので、typeパラメーターは。の境界に準拠しますA <: Foo[A]。しかし、現在、Foo不適合な型パラメーターを持つ内部があり、解がのようなものであることを示唆していますがFoo[Foo[Foo[Foo[...、これは明らかに正しくありません。

だから私の質問はおそらく次のように要約することができます:「この変数は正当なものを保持します」のScala構文は何Fooですか?

4

2 に答える 2

2

このような自己参照型のパラメーターは、適切ではないため、少し問題があります。たとえば、次のようなタイプを定義することができます。

case class BeerGarden extends Foo[Grill]

ご覧のとおり、A <:Foo[A]の境界は十分にタイトではありません。このような状況で私が好むのは、ケーキパターンと抽象型メンバーを使用することです。

trait FooModule {
  type Foo <: FooLike

  def apply(): Foo

  trait FooLike {
    def echo: Foo
  }
}

これで、Fooタイプを再帰的かつ安全に使用できます。

object Foos {
  def echo(foo: FooModule#Foo) = foo.echo
}

明らかに、これはそのようなタイプで解決したいすべての問題に対する理想的な解決策ではありませんが、重要な観察は、FooLikeは拡張可能な特性であるため、必要なメンバーを追加するためにFooLikeをいつでも改良し続けることができるということです。タイプメンバーが強制しようとしている境界に違反することなく。私が表現したいタイプのセットが閉じられていない実際のすべてのケースで、これは人ができる最善のことであることがわかりました。重要なのは、FooModuleが「自己型」を適用しながら、型とインスタンスの両方のコンストラクターを抽象化することです。もう一方を抽象化せずに一方を抽象化することはできません。

この種のことに関するいくつかの追加情報(および再帰型との私自身の初期の闘いの記録)はここで入手できます:

https://issues.scala-lang.org/browse/SI-2385

于 2012-07-23T19:34:42.830 に答える
0

ジェネリックの伝播の問題が存在することに同意しますが、この問題が発生すると、通常は設計が悪いことを示しているため、画面に大きな警告が表示されるはずです。これらは、トピックに関する一般的な提案です。

  • ジェネリックスを使用する場合、typeパラメーターは理由があります。タイプAのパラメーターを渡したり受け取ったりすることで、タイプセーフな方法でFoo [A]と対話でき、Aに制約を課すことができます。タイプ情報を失うと、タイプセーフが失われます。そのため、ジェネリックが不要になった場合、ジェネリッククラスを作成しても意味がありません。すべての署名をAnyに変更して、パターンマッチングを行うことができます。

  • ほとんどの場合、「typeclass」を使用してコレクションにCanBuildFromアプローチのようなものを実装することで、再帰型を回避できます。

  • 最後に、type-projection(FooModule#Foo)にはほとんどアプリケーションがないため、パスに依存する型を調べることをお勧めします。ただし、これらにもほとんど適用されません。

于 2012-07-24T07:00:28.727 に答える