単純な設計目標を実装しようとしていますが、Scala の型システムの複雑さに頭を悩ませています。Stream
Traversable、Iterator、Iterable、Stream、View などを比較した後、私の決定はカスタム トレイトを定義することです (簡潔にするために呼び出しましょう)。
- 非ジェネリックです(私のストリームは意味的にはいくつかの意味しか持たず、
Stream[StreamEntry]
のような無意味な型を避けたいですStream[Int]
) - と同様の使用法があります
Iterable
take
、などのすべてのメンバーは、基本的なものではなく、drop
返す必要があります。Stream
Iterable
これは私がこれまでに試したことです:
アプローチ1
ユース ケースを概説すると、簡単な例 (3 番目の設計目標に違反します) は次のようになります。
case class StreamEntry(data: Double) // just a dummy
trait Stream extends Iterable[StreamEntry] {
val metaInfo: String
}
// example use case
val s = new Stream {
val metaInfo = "something"
val iterator = StreamEntry(1) :: StreamEntry(2) :: StreamEntry(3) :: Nil toIterator
}
val t = s.take(1) // unfortunately, this is no longer a Stream
アプローチ 2
この 3 番目の要件では、ベース トレイトの代わりにテンプレート トレイトを使用する必要があります (これが、 SomeCollectionまたはSomeCollectionLikeを指す標準的な用語であることを願っています)。これは、 return に拡張するのと同じように、表現するコレクションの戻り値の型を再定義する whichを使用する必要があることを意味します。私の考えは、ほとんど同じことをすることでした。これは:IterableLike[StreamEntry, Stream]
Iterable
IterableLike[A, Iterable[A]]
Iterable
Iterable
// this is exactly the way `Iterable` is defined, but non-generic
trait Stream extends Traversable[StreamEntry]
with GenIterable[StreamEntry]
with GenericTraversableTemplate[StreamEntry, Stream]
with IterableLike[StreamEntry, Stream] {
...
}
残念ながら、これはコンパイルされません。これは、 のStream
テンプレート引数として表示されGenericTraversableTemplate
、コンパイラがテンプレート引数 (正確に 1 つ) を必要とするためStream
です。これは理にかなっています。
アプローチ 3、4、...
ここから、型システムに迷い込みました。削除するだけでは、 fromとの型パラメータの競合により、 のwith GenericTraversableTemplate
型が非互換になり、不正な継承が行われます。newBuilder
GenericTraversableTemplate
GenInterable
Traversable
おそらく、最も近い解決策は次のとおりです。
trait Stream extends TraversableLike[StreamEntry, Stream]
with IterableLike[StreamEntry, Stream] {
val metaInfo: String
def seq = this
def newBuilder: scala.collection.mutable.Builder[StreamEntry, Stream] = ???
}
これはコンパイルされますが、残念ながらビルダーを実装する方法がわかりません。非ジェネリック トレイトにジェネリック Builder を再利用することはできますか? Stream
実際には、他のコレクションから新しいものを実際に構築したくないので、ビルダーなしで行くことができます。しかし、現在、このアプローチで奇妙なランタイム動作が発生しており、完全には理解できません。例えば:
val s = new Stream {
val metaInfo = "something"
val iterator = StreamEntry(1) :: StreamEntry(2) :: StreamEntry(3) :: Nil toIterator
}
// executing the following independently (not in sequence) results in:
s.take(1) // throws: scala.NotImplementedError: an implementation is missing
// seems to require a Builder :(
s.toArray // works
s.toIterator // works
s.toIterable // throws: java.lang.ClassCastException: cannot be cast to scala.collection.Iterable
今、私は Scala 型システムの奥深さにいくぶん迷っています。この最後のアプローチで私はまだ正しい軌道に乗っていますか?ビルダーはこのパズルの欠けているピースにすぎませんか?
また、ビルダーの実装は、非ジェネリックで非キャッシュ型の場合、どのように見えるでしょうか? 実装する簡単なアイデアは+=
、変更可能なバッファーを使用することですが、これはそもそもイテレーターの使用に非常に反します...そして、to
そのクラスを構築する方法がわからない場合、メンバーをどのように実装する必要がありますか?タイプ?関連するコードはすべてライブラリのどこかにあるはずだと思いますが、それを掘り出すことはできません。