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 in scala.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?
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 the Parsers
class. In fact, the Parser
class itself depends on what input
has been defined on Parsers
:
abstract class Parser[+T] extends (Input => ParseResult[T])
The type Input
is defined like this:
type Input = Reader[Elem]
And Elem
is abstract. Consider, for instance, RegexParsers
and TokenParsers
. The former defines Elem
as Char
, while the latter defines it as Token
. That means the Parser
for the each is different. More importantly, because Parser
is a subclass of Parsers
, the Scala compiler will make sure at compile time you aren't passing the RegexParsers
's Parser
to TokenParsers
or vice versa. As a matter of fact, you won't even be able to pass the Parser
of one instance of RegexParsers
to another instance of it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With