を私はいくつかのポイントを持っていますまず第一に、特許請求の範囲は、特許請求の範囲第
それは私がconsと#:: behaviorsで使用した境界を持つことですが、すべてのMyStreamはMyStream [Any]に縮退します。
は実際にはtrueではありません。あなたはこのlive demoであなた自身を見ることができます。 ssGood
は、キャストの必要なしにタイプssGood2
に簡単に割り当てられ、MyStream[Any]
と明示的に入力されたssBad
ではどうやって行うことができないのか注意してください。ここでのポイントは、このシナリオではScalaコンパイラが型を正しく取得できることです。私は容疑者 Intellij IDEAは間違ったタイプを推測し、ハイライトなどを悪用しているということです。IDEAはコードを複雑にすると独自のコンパイラを使用します。場合によっては、コードが正しいかどうかを実際にコンパイルする必要があります。
第2のクレームについての第2のクレームも、私には間違っています。
しかし、私は素朴なジェネリックで行く場合:
def cons[T](h: T, t: => MyStream[T]): MyStream[T] = new MyStream[T] ...
タイプは安定したまま、私は短所/#を使用することはできません:: MyStreamには何も添付します。空の ...私は、次のコード(available online)
object MyStream {
val empty: MyStream[Nothing] = new MyStream[Nothing] {
override def isEmpty = true
override def head = throw new NoSuchElementException("tead of empty MyStream")
override def tail = throw new NoSuchElementException("tail of empty MyStream")
}
def cons[T](h: T, t: => MyStream[T]): MyStream[T] = new MyStream[T] {
def isEmpty = false
def head = h
lazy val tail = t
}
implicit class MyStreamOps[T](t: => MyStream[T]) {
def #::(h: T): MyStream[T] = cons(h, t)
}
}
abstract class MyStream[+T] {
def isEmpty: Boolean
def head: T
def tail: MyStream[T]
@tailrec final def foreach(op: T => Unit): Unit = {
if (!isEmpty) {
op(head)
tail.foreach(op)
}
}
}
import MyStream._
val ss0 = 1 #:: empty
val ss1: MyStream[Int] = ss0
val ss2: MyStream[Int] = 1 #:: empty
を使用する場合
それがコンパイルされていれば MyStream[+T]
宣言に[+T]
があるとして私のために[OK]を実行します。そして今度は、あなたが間違って何をしているのか分かりません(実際のコンパイラエラーを提供していないので、推測が難しいです)。
また、empty
が非ジェネリックで不変の場合は、def
である必要はありません。val
でもかまいません。
まだ問題が発生している場合は、その再生方法とエラーの詳細をお伝えください。
更新(コメントするに答える)
トビーは、申し訳ありませんが、私はまだあなたの問題#2を理解していません。あなたの質問やコメントとしてコンパイルされないコードの例を教えてください。
私の唯一の推測は何を意味することは、あなたがメインの答えのようにただ1つの汎用T
でコードを使用している場合、このようなコードの一部に障害が発生したということであるということである。
def test() = {
import MyStream._
val ss0: MyStream[String] = "abc" #:: empty
val sb = new StringBuilder
val ss1: MyStream[CharSequence] = ss0 //OK
val ss2: MyStream[CharSequence] = cons(sb, ss0) //OK
val ss3: MyStream[CharSequence] = sb #:: ss0 //Bad?
}
はい、これは本当ですAFAIUは、暗黙のラッパーをチェックするときにScalaコンパイラがすべてのジェネリック型のすべてのポーズ可能な置換を通過しようとせず、最も特定のものだけを使用するためです。したがってss0
はMyStreamOps[String]
に変換されますが、MyStreamOps[CharSequence]
には変換されません。この問題を解決するには、別の汎用タイプU >: T
をMyStreamOps
の#::
に追加する必要がありますが、cons
に追加する必要はありません。したがって、次のMyStream
定義
object MyStream {
val empty: MyStream[Nothing] = new MyStream[Nothing] {
override def isEmpty = true
override def head = throw new NoSuchElementException("tead of empty MyStream")
override def tail = throw new NoSuchElementException("tail of empty MyStream")
}
def cons[T](h: T, t: => MyStream[T]): MyStream[T] = new MyStream[T] {
def isEmpty = false
def head = h
lazy val tail = t
}
implicit class MyStreamOps[T](t: => MyStream[T]) {
//def #::(h: T): MyStream[T] = cons(h, t) // bad
def #::[U >: T](h: U): MyStream[U] = cons(h, t) //good
}
}
でさえss3
は(さえU
のない正確+T
動作するため、コンパイルcons
を使用し、ss2
)エラーなしでコンパイル。
オンポイント1では、魅力的です。私はこれについて正しい正当な礼儀が何であるかは分かりませんが、これに対処するための "答え"を追加します。 ポイント2では、私は本当にミスピークをしました - 私はしばらくの間2つの問題で戦っていました、そして第2は私の心の中で失われました。何が壊れているのは空の使用ではありませんが、 "子"クラスのストリームを作成し、それに "親"クラスを追加しようとすると失敗します。 + Tの部分が "親のストリームに代入可能"を扱うので、+ Tの部分は扱いますが、それはU>:T境界のことであり、#::がその場合に必要な親の型のストリームを返すことができる。 –
@TobyEggitt、申し訳ありませんが、私はまだあなたの問題#2を理解していません。ここでコンパイルされないコードの例を教えてください。私の答えは更新の詳細を参照してください。 – SergGr
まあ、私はここでそれをフォーマットしますが、この使用を考慮することはできません。 ヴァル・P =新しい親( "P")=新CHILD1 ヴァル・C1( "C1") ヴァルPS = P#:: MyStreamを。空白 val pc1 = c1#:: MyStream.empty val px = p#:: pc1 //これ! –