为什么 Scala API 有两种组织类型的策略?
我注意到 Scala 标准库使用两种不同的策略来组织类、特征和单例对象。
使用其成员已导入的包。例如,这就是访问 scala.collection.mutable.ListBuffer 的方式。这种技术在 Java、Python 等中很常见。
使用特征的类型成员。例如,这就是您访问
Parser
类型的方式。您首先需要混合scala.util.parsing.combinator.Parsers
。这种技术在 Java、Python 等中并不常见,并且在第三方库中使用不多。
我猜 (2) 的优点之一是它组织了方法和类型,但根据 Scala 2.8 的包对象,使用 (1) 也可以完成同样的事情。为什么要采用这两种策略?每种应该什么时候使用?
I've noticed that the Scala standard library uses two different strategies for organizing classes, traits, and singleton objects.
Using packages whose members are them imported. This is, for example, how you get access to
scala.collection.mutable.ListBuffer
. This technique is familiar coming from Java, Python, etc.Using type members of traits. This is, for example, how you get access to the
Parser
type. You first need to mix inscala.util.parsing.combinator.Parsers
. This technique is not familiar coming from Java, Python, etc, and isn't much used in third-party libraries.
I guess one advantage of (2) is that it organizes both methods and types, but in light of Scala 2.8's package objects the same can be done using (1). Why have both these strategies? When should each be used?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这里值得注意的命名法是路径相关类型。这就是你所说的选项 2,我只谈它。除非您碰巧用它解决了问题,否则您应该始终采用选项 1。
您错过的是
Parser
类引用Parsers
类中定义的内容。事实上,Parser
类本身取决于Parsers
上定义的input
:定义了
Input
类型像这样:Elem
是抽象的。例如,考虑一下RegexParsers
和TokenParsers
。前者将Elem
定义为Char
,后者将其定义为Token
。这意味着每个的Parser
是不同的。更重要的是,由于Parser
是Parsers
的子类,Scala 编译器将确保在编译时您不会传递RegexParsers
的Parser
到TokenParsers
或反之亦然。事实上,您甚至无法将RegexParsers
的一个实例的Parser
传递给它的另一个实例。The nomenclature of note here is path-dependent types. That's the option number 2 you talk of, and I'll speak only of it. Unless you happen to have a problem solved by it, you should always take option number 1.
What you miss is that the
Parser
class makes reference to things defined in theParsers
class. In fact, theParser
class itself depends on whatinput
has been defined onParsers
:The type
Input
is defined like this:And
Elem
is abstract. Consider, for instance,RegexParsers
andTokenParsers
. The former definesElem
asChar
, while the latter defines it asToken
. That means theParser
for the each is different. More importantly, becauseParser
is a subclass ofParsers
, the Scala compiler will make sure at compile time you aren't passing theRegexParsers
'sParser
toTokenParsers
or vice versa. As a matter of fact, you won't even be able to pass theParser
of one instance ofRegexParsers
to another instance of it.第二个也称为蛋糕模式。
它的好处是,混合有特征的类内的代码变得独立于该特征中方法和类型的特定实现。它允许使用特征的成员而不知道它们的具体实现是什么。
上面,
Logging
特征是对App
的需求(需求也可以用自我类型来表达)。然后,在应用程序中的某个时刻,您可以决定实现是什么,并将实现特征混合到具体的类中。与导入相比,这具有优势,因为您的代码段的要求不受
import
语句定义的实现的约束。此外,它允许您构建和分发一个 API,该 API 可以在其他地方的不同构建中使用,前提是通过混合具体实现来满足其要求。但是,使用此模式时需要注意一些事项。
我猜库设计者并没有将上述任何问题视为解析器所关心的问题。
The second is also known as the Cake pattern.
It has the benefit that the code inside the class that has a trait mixed in becomes independent of the particular implementation of the methods and types in that trait. It allows to use the members of the trait without knowing what's their concrete implementation.
Above, the
Logging
trait is the requirement for theApp
(requirements can also be expressed with self-types). Then, at some point in your application you can decide what the implementation will be and mix the implementation trait into the concrete class.This has an advantage over imports, in the sense that the requirements of your piece of code aren't bound to the implementation defined by the
import
statement. Furthermore, it allows you to build and distribute an API which can be used in a different build somewhere else provided that its requirements are met by mixing in a concrete implementation.However, there are a few things to be careful with when using this pattern.
I guess the library designers didn't regard any of the above as an issue where
Parsers
are concerned.