Until 2.8, the only things you could put in a package were classes, traits, and standalone objects. These are by far the most common definitions that are placed at the top level of a package, but Scala 2.8 doesn't limit you to just those. Any kind of definition that you can put inside a class, you can also put at the top level of a package. If you have some helper method you'd like to be in scope for an entire package, go ahead and put it right at the top level of the package.
To do so, you put the definitions in a package object. Each package is allowed to have one package object. Any definitions placed in a package object are considered members of the package itself.
An example is shown in the following listings. Assume first a class, Fruit, and three Fruit objects in a package gardening.fruits:
// in file gardening/fruits/Fruit.scala | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
package gardening.fruits | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
case class Fruit(name: String, color: String) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
object apple extends Fruit("Apple", "green") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
object plum extends Fruit("Plum", "blue") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
object banana extends Fruit("Banana", "yellow") |
Now assume you want to place a variable, planted, and a method, showFruit, directly into package gardening. Here's how you would do that:
// in file gardening/fruits/package.scala | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
package gardening | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
package object fruits { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val planted = List(apply, plum, banana) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def showFruit(fruit: Fruit) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
println(fruit.name +"s are "+ fruit.color) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
File gardening/fruits/package.scala holds a package object for package gardening.fruits. Syntactically, a package object looks much like a plain object definition. The only difference is that it includes the package keyword. It's a package object, not a plain object. The contents of the curly braces can include any definitions you like. In the listing above, the package object includes the planted variable and the showFruit utility method.
Given that definition, any other code in the same package can import the method just like it would import a class. For example, the following object, PrintPlanted, imports planted and showFruit in exactly the same way it imports class Fruit, using a wildcard import on package gardening.fruits:
// in file PrintPlanted.scala | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import gardening.fruits._ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
object PrintPlanted { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def main(args: Array[String]) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for (fruit: Fruit <- fruits.planted) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
showFruit(fruit) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
Package objects can contain arbitrary definitions, not just variable and method definitions. For instance, they are also frequently used to hold package-wide type aliases and implicit conversions. Package objects can even inherit from Scala classes and traits.
Package objects are compiled to class files named package.class which are the located in the directory of the package that they augment. So the package object fruits would be compiled to a class with fully qualified name gardening.fruit.package (Note that, even though package is a reserved word in Java and Scala, it is still allowed as part of a class name on the JVM level. In Scala, you can even define it directly, using backticks:
package gardening.fruits | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
object `package` { ... } |
The standard Scala package also has its package object. Because scala._ is automatically imported into every Scala file, the definitions of this object are available without a prefix.
Here are the most important definitions in this package object. As you can see, the main purpose of this object is to make a number of often-used definitions nested in subpackages available from the scala package. In this way, we distinguish convenience of access from logical structure. For instance, the List type is used so often that it makes sense to put it in the scala package thereby making it accessible without an import or name qualification. On the other hand, List is an immutable collection class, so it belongs logically in package scala.collection.immutable. Using package objects, you can have both a logical structure and fast access.
package object scala { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// type and value aliases for collection classes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type TraversableOnce[+A] = scala.collection.TraversableOnce[A] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type Traversable[+A] = scala.collection.Traversable[A] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val Traversable = scala.collection.Traversable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type Iterable[+A] = scala.collection.Iterable[A] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val Iterable = scala.collection.Iterable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type Seq[+A] = scala.collection.Seq[A] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val Seq = scala.collection.Seq | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type IndexedSeq[+A] = scala.collection.IndexedSeq[A] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val IndexedSeq = scala.collection.IndexedSeq | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type Iterator[+A] = scala.collection.Iterator[A] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val Iterator = scala.collection.Iterator | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type BufferedIterator[+A] = scala.collection.BufferedIterator[A] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type List[+A] = scala.collection.immutable.List[A] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val List = scala.collection.immutable.List | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val Nil = scala.collection.immutable.Nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type ::[A] = scala.collection.immutable.::[A] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val :: = scala.collection.immutable.:: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type Stream[+A] = scala.collection.immutable.Stream[A] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val Stream = scala.collection.immutable.Stream | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val #:: = scala.collection.immutable.Stream.#:: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type Vector[+A] = scala.collection.immutable.Vector[A] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val Vector = scala.collection.immutable.Vector | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type StringBuilder = scala.collection.mutable.StringBuilder | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type Range = scala.collection.immutable.Range | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val Range = scala.collection.immutable.Range | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Numeric types which were moved into scala.math.* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type BigDecimal = scala.math.BigDecimal | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val BigDecimal = scala.math.BigDecimal | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type BigInt = scala.math.BigInt | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val BigInt = scala.math.BigInt | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type Equiv[T] = scala.math.Equiv[T] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type Fractional[T] = scala.math.Fractional[T] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type Integral[T] = scala.math.Integral[T] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type Numeric[T] = scala.math.Numeric[T] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val Numeric = scala.math.Numeric | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type Ordered[T] = scala.math.Ordered[T] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val Ordered = scala.math.Ordered | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type Ordering[T] = scala.math.Ordering[T] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val Ordering = scala.math.Ordering | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type PartialOrdering[T] = scala.math.PartialOrdering[T] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type PartiallyOrdered[T] = scala.math.PartiallyOrdered[T] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
... | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
Note also that along with most of the type aliases in package scala comes a value alias of the same name. For instance, there's a type alias for the List class and a value alias for the List object. That way, you can not only access the type List without a prefix but also create list values with syntax such as List(1, 2, 3). If you decompose the latter expression you get a call to the apply method of the scala.List value:
List.apply(1, 2, 3) |
That value in turn is an alias of the scala.collection.immutable.List object, which defines the apply method in question.
An interesting question is what happens when the name of a top-level class or object in a package is redefined in the associated package object. For the moment, this is simply forbidden. But if we wanted to allow this, it would make sense to take the definition in the package object as the public interface of the definition in the package itself. Code from within the package could see the full definition, but code from outside the package could only see what's defined in the package object. That way, package objects could evolve into the backbone of a fairly advanced module system, which allow you to flexibly combine interfaces with implementations.
Of course, all of this remains to be done, and the details remain to be worked out, but it opens up interesting possibilities.
Martin Odersky is coauthor of Programming in Scala: http://www.artima.com/shop/programming_in_scala |
The Scala programming language website is at:
http://www.scala-lang.org
The Scala 2.8 release notes are at:
http://www.scala-lang.org/node/7009
The Scaladoc collections API is at:
http://lampwww.epfl.ch/~odersky/whatsnew/collections-api/collections.html
Have an opinion? Readers have already posted 3 comments about this article. Why not add yours?
Martin Odersky is the creator of the Scala language. As a professor at EPFL in Lausanne, Switzerland, he works on programming languages, more specifically languages for object-oriented and functional programming. His research thesis is that the two paradigms are two sides of the same coin, to be unified as much as possible. To prove this, he has experimented with a number of language designs, from Pizza to GJ to Functional Nets. He has also influenced the development of Java as a co-designer of Java generics and as the original author of the current javac reference compiler. Since 2001 he has concentrated on designing, implementing, and refining the Scala programming language.
Artima provides consulting and training services to help you make the most of Scala, reactive
and functional programming, enterprise systems, big data, and testing.