1

抽象型付けを使用して型処理を簡素化および明確化しようとしていますが、次のような一見無意味なエラーに遭遇し続けています。

trait Delta[A] {
  def apply(c: A)
}

abstract class ValueHolder {

  type Value
  type Event <: Delta[ValueHolder]

  def update(next: Value): Event = new UpdateEvent(next)
  // type mismatch;  found   : UpdateEvent[ValueHolder]
  // required: ValueHolder.this.Event
}

class UpdateEvent[C <: ValueHolder](next: C#Value) extends Delta[C] {

  def apply(c: C) = c.update(next)
  // type mismatch;  found   :
  // UpdateEvent.this.next.type (with underlying type C#Value)
  // required: c.Value
}
  1. Delta[C]したがって、どこC <: ValueHolderに準拠していませんEvent <: Delta[ValueHolder]か?

  2. 同様に、 が であるcとすると、 ではありCませんか?c.ValueC#Value

キャストを使用して 2 番目のエラーを取り除くことはできますが、それでは型を使用する意味がなくなります。

[この関連する質問] [1]で提案された回答を取り入れようとしました...

class UpdateEvent[C <: ValueHolder, V <: C#Value](next: V) extends Delta[C] {

... 残念ながら、どちらの問題も軽減できません (ただし、update() から呼び出されると、さらにいくつかの型パラメーターが必要になります)。

ヘルプ???


更新:残念ながら、上記の例は少し単純化されすぎていました。私は、元の「ビュー」として機能する同じメソッド シグネチャを持つクラスに変更を伝達しようとしています (ただし、型パラメーターが異なる可能性があります)。

たとえば、これを実行できると想像してください。

(ListBuffer[Int]:_).map(_.toString)

...そしてListBuffer[String]、オリジナルが更新されるたびに結果が更新されます。(単に「map」を何度も実行しないと、理由は簡単に説明できません。) この小さな例のように、他の人が実装されるトレイトを定義します。つまり、問題を回避するためにメソッド シグネチャを変更することはできません。

type Event(注:それぞれを受け取るすべてのリスナーを含む変数(ここには示されていません)があるため、私も取り除くことはできません。Eventそのタイプは、サブクラスによって洗練され、それぞれのより具体的な種類のリスナーを許可する必要があります。)

とにかく、あまり説明的ではない Scala リファレンス マニュアル (情報はすべてそこにありますが、既に多くのことを知っていることを前提としています) を熟考した後、UpdateEventC と C#Value が対応するように制約する方法を最終的に見つけました。

class UpdateEvent[V, C <: ValueHolder { type Value = V }](
  next: V) extends Delta[C] { ... }

これにより、コンパイル エラーが修正され、既存のアプローチが保持されます。しかし、私はピーターの答え (以下) を正しいものとしてマークしています (彼に評判ポイントを与えます)。ありがとう、ピーター。

4

1 に答える 1

2

Delta[C]したがって、どこC <: ValueHolderに準拠していませんEvent <: Delta[ValueHolder]か?

私はこれについて完全には確信が持てないので、理論として次のことを理解してください。コンパイラは、 の型パラメーターが共変 (つまり) である場合にのみDelta[C]、 が であるC <: ValueHolderことを保証できます。しかし、現在は不変であるため、上記は当てはまりません。コンパイラは、 aが:が抽象型であるかどうか、つまり、後で定義される型のプレースホルダーであるかどうかもわかりません。これは のサブタイプになります。ただし、 !だけでなく、そのようなサブクラスとして定義することもできます。したがって、 の型を持つサブクラスを定義することができますが、これは ではありません。コンパイラがこれを許可した場合、最終的な結果は実行時エラーになります。Delta[ValueHolder]DeltaDelta[+A]Delta[C]EventEventDelta[ValueHolder]UpdateEventValueHolderEventOtherEventUpdateEvent

同様に、 が であるcとすると、 ではありCませんか?c.ValueC#Value

確かにそうです。しかし、エラーメッセージを見てください:

  // type mismatch;  found   :
  // UpdateEvent.this.next.type (with underlying type C#Value)
  // required: c.Value

つまり、コンパイラは の型を必要c.Valueとしているのに、代わりに を取得しましC#Valueた。とC#Value異なりますc.Value- 前者はより一般的なタイプです!

潜在的な解決策の一部はupdate、バインドされた型 parameterでパラメーター化され、 の代わりにパラメーター型としてC <: ValueHolder使用される場合があります。これにより、2 番目のエラーが解消されます。最初の問題の解決策は、 の戻り値の型を に置き換えることです。したがって、次のようにコンパイルされます。C#ValueValueHolder#ValueEventDelta[C]

trait Delta[A] {
  def apply(c: A): Delta[A]
}

abstract class ValueHolder {

  type Value

  def update[C <: ValueHolder](next: C#Value): Delta[C] = new UpdateEvent(next)
}

class UpdateEvent[C <: ValueHolder](next: C#Value) extends Delta[C] {

  override def apply(c: C) = c.update(next)
}

ノート:

  • このスキームでEventは、実際にはもう使用されていないため、完全に削除できます
  • 実際に返される型に準拠するために、Delta.applyasの戻り値の型を定義しましたDelta[A]UpdateEvent.apply
  • このため、型パラメーターAはここでのみ不変である可能性があります。これAは、 内で共変 (メソッドの戻り値の型として) と反変 (メソッドのパラメーターとして) の両方の位置に立つためDeltaです。

これがお役に立てば幸いです-あなたが何を達成しようとしているのかわからないので、これらの変更であなたの元のアイデアを虐殺したかもしれません:-)

于 2013-01-22T15:25:24.047 に答える