1

我试图实现一个简单的设计目标,但是 Scala 类型系统的复杂性让我有些头疼。在比较了 Traversable、Iterator、Iterable、Stream、View 等之后,我的决定是定义一个自定义特征(为了简洁起见,我们称之为Stream

  • 是非泛型的(我的流在语义上只有一些有意义Stream[StreamEntry],我想避免像这样无意义的类型Stream[Int]
  • 有类似的用法Iterable
  • 所有成员,如take,等都drop应该返回Stream,而不是基本的Iterable

这是我到目前为止所尝试的:

方法一

为了勾勒用例,一个简单的例子(违反了第三个设计目标)是:

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

方法二

第三个要求要求使用模板特征而不是基本特征(我希望这是引用SomeCollectionSomeCollectionLike的标准术语)。这意味着我必须使用IterableLike[StreamEntry, Stream]which 重新定义表示集合的返回类型,就像IterableextendsIterableLike[A, Iterable[A]]到 return一样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并且编译器现在需要一个模板参数(正好是一个)Stream,这是有道理的。

方法 3、4、...

从这里开始,我迷失在类型系统中。由于from和. with GenericTraversableTemplate_ _newBuilderGenericTraversableTemplateGenInterableTraversable

也许最接近的解决方案如下:

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。是否可以为我的非通用特征重用通用生成器?实际上,我虽然可以不使用 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 类型系统的深度感到有些迷失。我是否仍然在使用最后一种方法的正确轨道上,而 Builder 是否只是这个难题中缺失的部分?

对于非泛型非缓存类型,Builder 实现会是什么样子?一个简单的实现想法+=是使用一些可变缓冲区,但这首先会非常反对使用迭代器......to如果我不知道如何构造一个类,我应该如何实现该成员类型?我想所有相关的代码都必须在图书馆的某个地方,我就是无法挖掘出来。

4

1 回答 1

2

哇!你那里有很多事情要做...

以下是您在解决此设计问题时应该了解或考虑的一些事项...

术语:

  • 我们不指“模板”,我们称它们为“通用”或“参数化”类型。原因是这些类型不是模板!也就是说,每次使用它们时,它们都没有填充它们的实际类型参数来创建新类(就像 C++ 中的情况一样,它正确地使用了术语“模板”)。相反,只创建一个类 (*),它使用特定类型参数为该泛型类型的每个实例提供服务。

设计和语言因素:

你说:

... 是非泛型的(我的流在语义上只对某些 Stream[StreamEntry] 有意义,我想避免像 Stream[Int] 这样的无意义类型)

该要求支持非泛型类。相反,它是“类型绑定”的本质。例如:

class Generic[T <: UpperBound](ctorArgs...) {
}

在这种情况下,类Generic只能被实例化为 的子类型UpperBound。(请注意,每当我们说“子类型”时,我们指的是自反子类型关系。换句话说,在此定义下,每种类型都是其自身的子类型。

结果:

我想知道 Scala 标准库中的现有类型不满足您的“流”类是什么、做什么或有什么?正如您所发现的那样,扩展标准库集合类并不是一件容易的事,尽管它确实是可行的。我认为这样做不是 Scala 编程中的基本练习,并且可能不应该作为您首次涉足 Scala 的尝试之一。

(*) 这是一种过度简化,足以满足本说明的目的。

于 2013-02-11T15:36:43.237 回答