Bay-Area Scala Enthusiasts (BASE) Meeting: What's New In Scala 2.8 139

Posted by Dean Wampler Fri, 05 Jun 2009 07:13:00 GMT

This week is JavaOne in San Francisco. The Bay-Area Scala Enthusiasts (BASE) held their monthly meeting. Martin Odersky, the creator of Scala, was the special guest. He discussed what’s new In Scala 2.8, followed by Q&A. We met at Twitter HQ.

These are my notes, focusing primarily on Martin’s presentation, and filled in afterwards with additional details. Any transcription errors or erroneous extrapolations are my own fault. It’s also late in the day…

Some of the features are not yet in the SVN trunk, so don’t assume my examples actually work! See the scala-lang.org for more details on Scala 2.8 features.

There are a few more months before it is released. A preview is planned for July, followed by the final release in September or October.

New Features

Here are the new features for this release.

Named and Default Arguments

Scala method parameters can be declared to with default values, so callers don’t have to specify a value and the implicit convention doesn’t have to be used. The default “values” aren’t limited to constants. Any valid expression can be used. Here is an example that I made up (not in Martin’s slides) that illustrates both specifying and using one default argument and using named arguments.

    
def joiner(strings: List[String], separator: String = " ") = strings.mkString(separator)

val strs = List("Now", "is", "the", "time", "for", "all", "good", "men", "...")
println(joiner(strs))
println(joiner(strs, "|"))
println(joiner(strings = strs, separator = "-"))
    

Named and default arguments enable an elegant enhancement to case classes. It’s great that I can declare a succinct value class like this.

    
case class Person(firstName: String, lastName: String, age: Int)
    

What if I want to make a copy that modifies one or more fields. There’s no elegant way to add such a method in 2.7 without implementing every permutation, that is every possible combination of fields I might want to change. The new copy method will make this easy.

    
case class Person(firstName: String, lastName: String, age: Int)

val youngPerson = Person("Dean", "Wampler", 29)
val oldPerson = youngPerson copy (age = 30)
    

I’m using the infix notation for method invocation on the last line (i.e., it’s equivalent to ... youngPerson.copy(...)). I can specify any combination of the fields I want to change in the list passed to copy. The generated implementation of copy will use the current values of any other fields as the default values.

The implementation looks something like this.

    
case class Person(firstName: String, lastName: String, age: Int) {
  def copy (fName: String = this.firstName, 
            lName: String = this.lastName, 
            aje: Int = this.age) = new Person(fName, lName, aje)
}
    

Quite elegant, once you have default and named arguments!!

Defaults for parameters can’t refer to previous parameters in the list, unless the function is curried. (I’m not sure I got this right, nor do I understand the reasons why this is true – if it’s true!)

By the way, Martin reminded us that method parameters are always evaluated left to right at the call site. Do you remember the rules for Java, C++, C#,...?

Nested Annotations

Annotations can now be nested, which is important for using some of the standard annotation definitions in the JDK and JEE. This feature also exploits named and default arguments.

    
@Annotation1(foo = @Annotation2)
    

Package Objects

People have complained that they want to define top-level definitions for a package, but they have to put those definitions, like types and methods, in an object or class, which doesn’t quite fit and it’s awkward for referencing through package and type qualification. The problem was especially obvious when the team started working on the major reorganization of the collections (discussed below). So, Scala 2.8 will support “package objects”.

    
package object scala {
  type List[+T] = scala.collection.immutable.List
  val List = scala.collection.immutable.List
}
    

Our friend List is now moved to scala.collection.immutable.List, but we would still like to reference it as if it were in the scala package. The definition defines a package-level type and val the effectively make List accessible in the scala scope. In Scala 2.7 you would have to do something like the following (ignoring Predef for a moment).

    
package scala {
  object toplevel {
    type List[+T] = scala.collection.immutable.List
    val List = scala.collection.immutable.List
  }
}
    

But then you would have to reference List using scala.toplevel.List.

Now, they got around this problem previously by putting a bunch of stuff like this in Predef and importing it automatically, but that has several disadvantages.

  • Predef is a big, amorphous collection of stuff.
  • You can’t define your own Predef with the same convenient usage semantics, i.e., no special import required and no way to reference definitions like package.type. You would have to use the alternative I just showed with toplevel in the middle.

Package objects give you a place for definitions that you want to appear at the package scope without having to define them in a singleton object or class.

Finally, besides types and fields as shown, package objects can also define methods. They can also inherit from traits and classes.

@specialized

Scala generics are fully specified at declaration time using a uniform representation, not when they are used, like C++ templates. This supports the way Java works, where there isn’t a giant link step to resolve all references, etc. However, this has a major performance disadvantage for generic types when they are actually used with AnyVal types that Scala optimizes to primitives.

For example, any closures require the use of FunctionN[T1, T2, ...], e.g.,

    
def m[T](x: T, f: T => T) = f(x)

m(2, (x:Int) => x * 2)
    

The f closure in the definition of m will require instantiation of Function2[T,T]. However, when use AnyVal classes, as in the last line , this has the effect of causing primitive boxing and unboxing several times, hurting performance when this is completely unnecessary in the special case of primitives being used. This is also bad for arrays and some other data structures.

The new @specialized annotation fixes this problem by causing scala to generate different versions of the user-specified generic type or method for each of the primitive types.

    
def m[@specialized T](x: T, f: T => T) = f(x)

m(2, (x:Int) => x * 2)
    

There is a real risk of an explosion of code. Consider what would have to be generated to support every type permutation for Function22! For this reason they only do cases with up to two type parameters in the library. You can also choose to annotate only some of the type parameters, as appropriate, and the annotation will support parameters that let you limit the primitive types that will be supported, e.g., only Ints and Longs.

This feature is not yet in the 2.8 trunk, but it will be soon.

Improved Collections

Collections are getting a major revamp. First they want to eliminate gratuitous differences in package structure and implementations. In many cases, the map method and others have to be redefined for each basic collection type, rather than shared between them.

New Collections Design

The new version of the library will support the following.

  • Uniform structure.
  • Every operation is implemented only once.
  • Selection of building blocks in a separate package called scala.collection.generic. These are normally only used by implementers of immutable and mutable collections.

Because of the reorganization, some Scala 2.7 source code won’t be compatible with 2.8 without modifications.

Better Tools

  • The REPL will have command completion, in addition to other enhancements.
  • They have greatly improved the IDE and compiler interface. Miles Sabin and Iulian Dragos worked on this with Martin. There is limited and somewhat unstable support in Eclipse now.

New Control Abstractions

Several new control abstractions are being introduced.

  • Continuations will be supported with a compiler plugin.
  • Scala has not had the break keyword. It will now exist, but as a library method.
  • Scala will optimize trampolining tail calls (e.g., foo1 tail calls foo2, which tail calls foo1, and back and forth).

More features

  • The Swing wrapper library has been enhanced.
  • The performance has been improved in several ways.
    • Structural type dispatch
    • Actors
    • Vectors, sets, and maps. Their long-term goal is to implement the fastest ones available for the JVM.

These changes are not yet in the trunk.

Beyond 2.8

Longer term, they plan significant improvements in support for parallelism and concurrency, including new concurrency models besides actors, such as:
  • Transactions (STM)
  • Data parallelism
  • stream processing

Clojure is influencing this. Martin praised the competition ;) Fortunately, the original designer of the data structures and algorithms used heavily by Clojure is working on Scala versions. (Name?)

Doug Lea wants to work with the team on concurrency data structures. The lack of closures in Java makes this effort difficult in Java.

There is some exciting work in advanced type system support for guaranteeing actor isolation and effect tracking. For example, this technology wouuld allow actors to exchange references to big objects without copying them while ensuring that they aren’t modified concurrently.

On a final note, Bill Wake described a conversation he had with Joshua Bloch today who admitted that the time has arrived for him to look seriously at Scala. A possible endorsement from Joshua Bloch would be a major step for Scala.