The Artima Developer Community
Sponsored Link

Scalazine
What's New in Scala 2.8: Package Objects
by Martin Odersky
September 14, 2010

Advertisement

Summary
The third installment of a series of articles on the latest Scala release, Scala 2.8, Martin Odersky explains package objects.

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.

Where to put package objects

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` { ... }

It's useful to keep the same convention for source files. So you would typically put the source file of the package object gardening.fruits into a file named package.scala that resides in the gardening/fruits directory.

The Scala package object

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(123)

That value in turn is an alias of the scala.collection.immutable.List object, which defines the apply method in question.

Things to come?

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.

Share your opinion

Have a question or opinion about Scala's collections API? Discuss this article in the Articles Forum topic, What's New in Scala 2.8: Package Objects.

Resources

front cover Programming in Scala 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

About the author

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.



Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us