prose :: and :: conz


Scala imports

Thanks to my most recent post on my love affair with Scala, I realize that it is time to write about Scala’s import. My Java background had me mostly unenthused about import because it doesn’t do much. An import merely brings a class name into the local namespace, preventing the developer from having to use the fully-qualified name within the code. For example, let’s say you want to use Java’s Random for generating pseudo-random numbers. Without an import, you would need the full name:

java.util.Random gen = new java.util.Random();
int r = gen.nextInt();

The import brings Random into the namespace of this Java file, allowing us to simply declare a Random:

import java.util.Random;

// Ceremonial Java stuff...

Random gen = new Random();
int r = gen.nextInt();

This is much nicer than typing out that whole name. I never imagined needing more than that from an import until Scala opened my eyes to the full potential of the import construct. Scala begs the question, “Why stop there?” One can import more than just class names. For instance, you can import a class itself. Here we import Scala’s Random:

import scala.util.Random._

val r = nextInt()

If you looked at the scaladoc for Random, you’ll see that like Java’s Random, it has a nextInt() function defined. Since we imported the object (for the uninitiated, think of this as a singleton or an abstract class with all static methods) with the wildcard character _, all of the members of that class are now in our namespace. We’re able to just call them as if they are our own methods. This is a great way to define some constants and share them across other classes. I find myself really missing this in my Java development. The Java work around is to define an interface with the constants you want defined as static final fields:

public interface MyFavorites {
    public static final int number = 42;
    public static final int color = "Crimson";
}

public class FavoritePrinter implements MyFavorites {
    public static int main(String[] args) {
        System.out.println("My favorite number is "+number);
        System.out.println("My favorite color is "+color);
        return 0;
    }
}

This has a distinct disadvantage to the Scala approach in that it causes my FavoritePrinter class to also be of type MyFavorites. It makes more sense if MyFavorites is merely an object with my favorite stuff in it, and my FavoritePrinter is its own thing:

object MyFavorites {
  val number = 42
  val color = "Crimson"
}

class FavoritePrinter {
  import MyFavorites._

  Console.println(s"My favorite number is $number")
  Console.println(s"My favorite color is $color")
}

Notice also that I imported MyFavorites within the class itself. That’s yet another feature of Scala’s import. You’re not limited to importing into an entire file. You can import into the scope of a class or even a function.

Importing is also not limited to one of these declared objects. You can import an instance of a class. Let’s take the case where you have a function that prints a message to a stream. You can import the stream and call its methods as if they were your own:

import java.io.PrintStream

object Printer {
  def print(message:String, stream:PrintStream) {
    import stream._

    // println is imported from the stream argument
    println(message)
  }
}

With an import in Scala, there is no limit to what you can pull into your namespace. If there is something that you have to use a . as part of its name, you can import whatever is on the left hand side of the . and reference everything available in that scope.

Hopefully my Java developers are already impressed with the design of Scala’s import. You may be surprised to find that it doesn’t stop here. Not only can you import anything, you can even rename it. This certainly appeals to me as a math geek where everything has single-letter names. Let’s say I’m writing a class which uses a whole lot of lists. Maybe I don’t like typing all four letters. I can rename it to L via an import:

object ListStuff {
  import scala.collection.immutable.{List => L}

  val nums = L(1, 2, 3)
  val strs = L("A", "B", "C")
}

While this is a power that can certainly be used for evil, it has some very useful applications. The first one is to shorten those awfully long names we tend to create with Java. Even the language itself is guilty of this. Just take a look at the exceptions defined in java.lang. It’s not that long names are bad. In fact, they’re quite good because they describe the purpose of the class without comments or documentation. However, it’s annoying if it is a class or object that you use frequently.

Possibly even more useful than reducing the sizes of class or method names is how this allows us to resolve name conflicts. I had this exact issue at a previous job where I was using Graph for Scala alongside the TinkerPop stack. They both have classes named Graph. In Java, we’re stuck importing one and using the fully-qualified name for the other. However, in Scala we can rename them to keep our code looking neat.

object GraphStuff {
  import scalax.collection.{Graph => XGraph}
  import com.tinkerpop.blueprints.{Graph => TGraph}

  implicit def XtoT(x:XGraph):TGraph = //...
}

If you’ve written a decent amount of Scala, there is a good chance you’ve already reused some Java libraries which utilize Java’s collections. In the case you’ve likely given the Java collections their own names in order to not conflict with the Scala collections. One of the language design goals behind Scala is to allow reuse of all existing Java libraries, so this ability to rename comes in handy quite often.

This subtle feature of the Scala language is another good example of how well thought-out Scala is. Rather than merely reeling a class into the local namespace, Scala’s import is a total solution to naming scopes.

Tagged with: scala (41), java (22)