Scala REPL tips and tricks (trunk only)

Arjan Blokzijl

The path on the road to learning Scala usually involves using the REPL. this is a very handy way of trying out functions you write easily and quickly, without having to set up an entire IDE environment. Scala's latests stable release Scala 2.7.5. Scala 2.8 will be out in a couple of months, as things stand now. This new release will contain a number of enhancements and new features, and if you can't wait to try this out, you should check out and use the trunk. I've previously blogged about starting with Scala, and also hinted at trying this out. In this blog, I'll show some more concrete examples of the and a few neat things that you can do if you're willing to take this step.

Get the trunk
To use the trunk, you can either check out the sources and build it yourself, our take the nightly builds. You've probably set a SCALA_HOME environment variable, so just point it to the newly created build and fire up the REPL. If all has gone well, you should see something like the following:

Welcome to Scala version 2.8.0.r18341-b20090718103640 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_14).
Type in expressions to have them evaluated.
Type :help for more information.
scala>

Using the trunk, the first thing you can profit from is using the redesigned and improved Scala collection library.
It has undergone a large redesign effort, largely done by Martin Odersky himself. It has been checked in and been living in the trunk for a while now, so using the trunk you can have the advantage working with it. The new API is explained in the Scala Improvement Proposal, an excellent and must read for understanding the design of Scala's collections.

Using the revamped REPL
Paul Phillips not only solves one bug after the other in the trunk, he also has the habit of pimping the REPL on a regular basis.
A very nice feature is that there is code completion, very handy if you don't have the complete Scala API in your head. First, define a variable and after typing the variable (and possibly the start of a function) hit tab:

scala> val l = List(1,2,3)
l: List[Int] = List(1, 2, 3)
scala> l 
AbstractStringBuilder       Annotation                  Appendable                  Application
Array                       AssertionStatusDirectives   BigDecimal                  BigInt
Boolean                     Byte                        Cell                        CharSequence
Character                   CharacterData00             CharacterData01             CharacterData02
CharacterData0E             CharacterDataLatin1         CharacterDataPrivateUse     CharacterDataUndefined
Class                       ClassLoader                 ClassfileAnnotation         Cloneable
Comparable                  Compiler                    ConditionalSpecialCasing    Console
CountedIterator             Deprecated                  Double                      Either
Enum                        Enumeration                 Equiv                       Float
Fractional                  Function                    GenericRange                Immutable
InheritableThreadLocal      Integer                     Integral                    Iterable
Left                        Long                        Math                        Mutable
None                        NotNull                     Number                      Numeric
Object                      Option                      Ordered                     Ordering
Override                    Package                     PartialFunction             PartialOrdering
PartiallyOrdered            Predef                      Process                     ProcessBuilder
ProcessEnvironment          ProcessImpl                 Product                     Proxy
Range                       RangeToString               Readable                    Responder
Right                       Runnable                    Runtime                     RuntimePermission
ScalaObject                 SecurityManager             SerialVersionUID            Short
Shutdown                    Some                        StackTraceElement           StaticAnnotation
StrictMath                  String                      StringBuffer                StringBuilder
StringCoding                SuppressWarnings            Symbol                      System
SystemClassLoaderAction     Terminator                  Thread                      ThreadDeath
ThreadGroup                 ThreadLocal                 Throwable                   TypeConstraint
UNIXProcess                 Unhashable                  UniquenessCache             Void
actors                      annotation                  ch                          cloneable
collection                  com                         compat                      concurrent
dbc                         deprecated                  inline                      instrument
io                          java                        javax                       jline
l                           management                  mobile                      native
net                         noinline                    org                         package
ref                         reflect                     remote                      runtime
scala                       serializable                settings                    specialized
sun                         sunw                        swing                       testing
text                        throws                      tools                       transient
unchecked                   unsealed                    util                        volatile
xml

scala> l.zip

zip            zipAll         zipWithIndex

Pretty neat. But that's not all: the trunk REPL now contains a power user mode:

scala> :help
All commands can be abbreviated - for example :h or :he instead of :help.

:help prints this help message.
:jar add a jar to the classpath.
:load followed by a filename loads a Scala file.
:power enable power user mode.
:quit exits the interpreter.
:replay resets execution and replays all previous commands.
:silent disable/enable automatic printing of results.

scala> :power
** Power User mode enabled - BEEP BOOP      **
** New vals! Try interpreter.          **
** New defs! Try mkType("T", "String")      **
** New cmds! :help to discover them         **

:help prints this help message.
:jar add a jar to the classpath.
:load followed by a filename loads a Scala file.
:power enable power user mode.
:quit exits the interpreter.
:replay resets execution and replays all previous commands.
:silent disable/enable automatic printing of results.
:dump displays a view of the interpreter's internal state.
:tree displays ASTs for specified identifiers.

Let's try some of this. Using mkType we can define a type alias, just as Haskell:

scala> mkType("IntList", "List[Int]")
defined type alias IntList
res3: scala.tools.nsc.InterpreterResults.Result = Success

scala> val il: IntList = List("1","2","3")
:5: error: type mismatch;
 found   : java.lang.String("1")
 required: Int
       val il: IntList = List("1","2","3")
       
       
scala> val il:IntList = List(1,2,3,4)     
il: IntList = List(1, 2, 3, 4)

scala> il.foldLeft(0)(_+_)
res5: Int = 10

Works like a charm.

Next, the :tree command displays some AST for classes and functions you've defined, if you're interested in this:

scala> def sum(a: Int)(b: Int)(c: Int) = a + b + c
sum: (a: Int)(b: Int)(c: Int)Int

scala> :tree sum
def sum(a: Int)(b: Int)(c: Int) = a.$plus(b).$plus(c) (DefDef)

Last but not least, we can make use of Scala's new cutting edge features. For instance, Scala has limited support for applying tail call optimizations at compile time. Determining whether the compiler actually will perform this optimization can be a tricky business however. If you're using this, but are unsure whether a function that you have written will actually be optimized by the compiler, you can use the @tailrec annotation now. For instance (the example is taken from the Programming in Scala book) the boom function shown below is not tail recursive, because of the increment function at the end. The bang function on the other hand is.

import scala.annotation.tailrec
class Tails {
  @tailrec def boom(x: Int): Int = {
  if (x == 0) throw new Exception("boom!")
  else boom(x-1)+ 1
}

  @tailrec def bang(x: Int): Int = {
  if (x == 0) throw new Exception("bang!")
  else bang(x-1)
  }
}

Using the @tailrec annotation on both, the compiler gives the following error:

:8: error: could not optimize @tailrec annotated method
         @tailrec def boom(x: Int): Int = {
                      ^
:13: error: could not optimize @tailrec annotated method
         @tailrec def bang(x: Int): Int = {

A bit confusing at first sight, since we get compilation errors for both methods, while we were pretty sure the bang method should be tail recursive. However, this can be explained by the fact that the bang method is defined in a class as public and not final. This means it could be overridden in a subclass, thereby preventing tail call optimization. Subtleties like this really makes this annotation quite useful. If we change the class into an object (for which methods can't be overridden), and remove the @tailrec annotation from the boom method, all compiles fine.

For more tail call and trampolining explorations in Scala, Rich Dougherty's blog is a good place to start. Also check out the Scala's TailRec class, which is available in the trunk, and this thread for discussion of it.

I haven't touched all of the new features that will be in Scala 2.8.0, but some a basic overview can be found here. Take a look and enjoy.

Comments (3)

  1. Erik Post - Reply

    August 30, 2009 at 1:20 am

    Hi Arjan, great article, thanks :D

  2. Jan Hutten - Reply

    May 28, 2010 at 9:31 pm

    Hi Arjen,

    Great article, thx ;).

  3. repl下的几种模式 | 在路上 - Reply

    May 15, 2013 at 7:57 am

    [...] power mode 支持的命令和方法经常变化,比如这里和这里提到的tip都已发生了变化。这个模式并不针对普通用户,并且cpu耗费很高,通常用不着。 [...]

Add a Comment