Scala继承;生成器麻烦;非通用IterableLike [英] Scala Inheritance; Builder Trouble; Non-Generic IterableLike

查看:230
本文介绍了Scala继承;生成器麻烦;非通用IterableLike的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现一个简单的设计目标,但Scala类型系统的复杂性给我带来了一些麻烦。在对Traversable,Iterator,Iterable,Stream,View等进行比较之后,我的决定是定义一个自定义特征(简单地称之为 Stream

I'm trying to implement a simple design goal, but the complexity of Scala's type system gives me some headache. After a comparison of Traversable, Iterator, Iterable, Stream, View etc my decision is to define a custom trait (let's just call it Stream for brevity) that


  • 是非泛型的(我的流在语义上只有一些 Stream [StreamEntry] 而且我想要避免无意义的类型,如 Stream [Int]

  • Iterable

  • 所有成员如采取 drop 等应返回 Stream 而不是基本 Iterable

  • is non-generic (my stream semantically only makes sense as some Stream[StreamEntry] and I want to avoid meaningless types like Stream[Int])
  • has similar usage to Iterable
  • all members like take, drop, etc should return Stream and not the basic Iterable.

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

This is what I have tried so far:

草绘使用case,一个简单的例子(违反了第三个设计目标)将是:

To sketch the use case, a simple example (which violates the third design goal) would be:

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



第三个要求要求使用模板特征而不是基本特征(我希望这是引用 SomeCollection SomeCollectionLike )的标准术语。这意味着我必须使用 IterableLike [StreamEntry,Stream] 重新定义代表集合的返回类型,就像 Iterable 一样扩展 IterableLike [A,Iterable [A]] 返回 Iterable s。我的想法与 Iterable 完全一样。这将是:

Approach 2

This third requirement calls for the usage of a template trait instead of the base trait (I hope this is the standard terminology to refer to either SomeCollection or SomeCollectionLike). This means I have to use IterableLike[StreamEntry, Stream] which redefines the return types of the representing collection just as Iterable extends IterableLike[A, Iterable[A]] to return Iterables. My idea was to do pretty much the same as Iterable does. This would be:

// 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 本身的模板参数(正好一个),这使得感觉。

Unfortunately, this does not compile because Stream appears as template argument to GenericTraversableTemplate and the compiler now requires a template argument (exactly one) for Stream itself, which makes sense.

从这里开始,我迷失了类型系统。只需使用GenericTraversableTemplate 删除就会导致不兼容的 newBuilder 类型,以及由于<中的类型参数冲突而导致的非法继承code> GenericTraversableTemplate 来自 GenInterable Traversable

Starting from here, I got lost in the type system. Just removing with GenericTraversableTemplate leads to an incompatible type of newBuilder and an illegal inheritance due to conflicts in the type parameters in GenericTraversableTemplate from GenInterable and Traversable.

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

Maybe the closest solution was the following:

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 。但是目前我正在经历这种方法的一些奇怪的运行时行为,我无法完全理解。例如:

This compiles but unfortunately I have no idea how to implement the Builder. Is it possible to reuse a generic Builder for my non-generic trait? Actually I though I can go without a Builder because I never actually want to build a new Stream from other collections. But currently I'm experiencing some strange runtime behavior with this approach, which I cannot fully understand. For instance:

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类型系统的深度感到有些迷茫。我是否仍然使用最后一种方法走上了正确的轨道,而构建器只是这个难题中缺失的部分吗?

Now I feel somewhat lost in the depth of the Scala type system. Am I still on the right track with this last approach and is the Builder just the missing piece in this puzzle?

对于非通用的非缓存类型,Builder实现如何?实现 + = 将使用一些可变缓冲区,但这将非常首先反对使用迭代器...如果我不知道如何构造该类的类,我应该如何实现成员?我想所有相关代码必须在库中的某个地方,我就是无法挖掘它。

And how would a Builder implementation look like for a non-generic non-cached type? A straightforward idea to implement += would be to use some mutable Buffer, but this would be very much against the use Iterators in the first place... And how should I implement the to member if I don't know how to construct a class of that type? I guess all relevant code must be somewhere in the library, I just can't dig it out.

推荐答案

哇!你有很多事情要去...

Wow! You've got a lot going on there...

以下是解决这个设计问题时你应该知道或考虑的一些事情......

Here are some things you should know or consider in solving this design problem...

术语:


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

  • We don't refer to "template"s, we call them "generic" or "parameterized" types. The reason for this is that these types are not templates! That is, they're not filled in with their actual type parameters to create new classes each time they're used (as is the case in C++, which rightly uses the term "template"). Instead, only one class is created (*) and it serves every instantiation of that generic type with particular type parameters.

设计&语言因素:

你说:


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

… is non-generic (my stream semantically only makes sense as some Stream[StreamEntry] and I want to avoid meaningless types like Stream[Int])

要求争论非泛型类。相反,它是类型绑定的本质。例如:

The requirement does not argue for a non-generic class. Rather it is the essence of what a "type bound" is. E.g.:

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

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

In this case, class Generic may only be instantiated with types that are subtypes of UpperBound. (Note that whenever we say "subtype" we mean a reflexive subtype relationship. In other words, every type is a subtype of itself under this definition.

结果:

我想知道你的流类是什么或者做了什么或有什么不满足现有类型Scala标准库?正如您已经发现扩展标准库集合类并不是完全无关紧要的,尽管它确实是可行的。我认为这样做不是Scala编程中的基本练习,可能不应该尝试作为您的一个首先进入Scala。

I wonder what your "stream" class is or does or has that is not satisfied by an existing type in the Scala Standard Library? As you've discovered extending the standard library collection classes is not entirely trivial, though it certainly is doable. I think doing so is not an elementary exercise in Scala programming and probably shouldn't be attempted as one of your first forays into Scala.

(*)这是一个过于简单化,足以解释这个问题。

(*) This is an oversimplification that suffices for the purpose of this explanation.

这篇关于Scala继承;生成器麻烦;非通用IterableLike的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆