Scala quickstart
This is a basic SBT project designed to help you start working effectively in a Scala REPL (read-eval-print loop) as quickly as possible. Once you've cloned the project, you can open the SBT console with the following command:
./sbt
Note that you'll need to have Java installed on your machine for this to
work. Once you're in the SBT console you can type console
to open a Scala
REPL. You should see something like this:
> console
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_66).
Type in expressions to have them evaluated.
Type :help for more information.
scala>
You can now type Scala definitions and expressions and they will be evaluated immediately.
REPL tips
Most of the time the REPL should just do what you'd expect it to do (try to evaluate code and print the result), but in some cases you may need or want to use special REPL-specific commands that it provides to accomplish certain things.
Defining companions
If you simply type the following definitions into the REPL as two separate lines, you'll get an error, since the case class and the object won't be treated as companions:
case class Foo(i: Int)
object Foo { def empty: Foo = Foo(0) }
In these situations you can use :paste
to combine definitions into a single
compilation unit:
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Foo(i: Int)
object Foo { def empty: Foo = Foo(0) }
// Exiting paste mode, now interpreting.
defined class Foo
defined object Foo
Copying and pasting entire session entries
Suppose you've got the following in a REPL session:
scala> val doubled = "a" * 2
doubled: String = aa
scala> doubled.size
res1: Int = 2
If you want to evaluate these definitions again (either in the same session or a
new one), you can copy and paste the entire block of text, including the
scala>
prompts and the result output, and the REPL will recognize that that's
what's going on:
scala> scala> val doubled = "a" * 2
// Detected repl transcript paste: ctrl-D to finish.
doubled: String = aa
scala> doubled.size
res1: Int = 2
// Replaying 2 commands from transcript.
Finding the type of an expression:
You can use the :type
command to make the REPL tell you the type of any
expression, even if that expression doesn't evaluate to a value:
scala> :type 1 / 0
Int
Saving a REPL session to a file
You can use :save
to write a REPL session to a file in the current directory:
scala> val doubled = "a" * 2
doubled: String = aa
scala> doubled.size
res0: Int = 2
scala> :save doubling.sc
doubling.sc
will now contain the following:
val doubled = "a" * 2
doubled.size
Note that these definitions won't necessarily be a valid .scala
file. I'm
using the .sc
extension here so that SBT won't try to parse the file as Scala,
but you can use any other extension you wish.
Loading Scala definitions into the REPL from a file
You can also use :load
to import definitions from a file into the REPL:
scala> :load doubling.sc
Loading doubling.sc...
doubled: String = aa
res0: Int = 2
Desugaring extension methods and other syntactic magic
Thanks to the magic of extension methods and implicit conversions, it's often very difficult to determine why you can call certain methods on certain expressions and where these methods are coming from. We've been writing the following, for example:
scala> val doubled = "a" * 2
doubled: String = aa
scala> doubled.size
res0: Int = 2
But Scala's String
is just an alias for java.lang.String
, which doesn't have
*
or size
methods. If we want to know where these are coming from, we can
use Scala's reflection API in the REPL to find out:
scala> import scala.reflect.runtime.universe.{ reify, showCode }
import scala.reflect.runtime.universe.{reify, showCode}
scala> showCode(reify("a" * 2).tree)
res0: String = Predef.augmentString("a").*(2)
Now we can look up the augmentString
method on Predef
in the standard
library's API docs, which will lead us to StringOps
.
This approach also works for syntactic sugar like for
-comprehensions. For
example, it might not be clear why we can mix and match sequences and options in
a for
-comprehension:
scala> showCode(reify(for { i <- 1 to 3; j <- Option(4) } yield i + j).tree)
This will print the following (which I've reformatted for clarity):
Predef.intWrapper(1).to(3).flatMap(
((i) => Option.option2Iterable(Option.apply(4).map(((j) => i.+(j)))))
)(IndexedSeq.canBuildFrom)
This takes some processing to understand, but at least everything is explicit.
Viewing bytecode and JVM method signatures
Many Scala language features (such as case classes) involve the generation of
synthetic methods, and even user-defined methods often end up having signatures
at the JVM level that you might not expect. It's possible to use :javap
in the
Scala REPL to inspect the signatures and bytecode generated by the Scala
compiler for any classes in your project (or that you have defined in the REPL):
scala> :javap -p IntOption
Compiled from "MyIntList.scala"
public interface IntOption {
public abstract <Z> Z fold(Z, scala.Function1<java.lang.Object, Z>);
}
scala> :javap -p SomeInt
Compiled from "MyIntList.scala"
public final class SomeInt implements IntOption,scala.Product,scala.Serializable {
private final int value;
public static scala.Option<java.lang.Object> unapply(SomeInt);
public static SomeInt apply(int);
public static <A> scala.Function1<java.lang.Object, A> andThen(scala.Function1<SomeInt, A>);
public static <A> scala.Function1<A, SomeInt> compose(scala.Function1<A, java.lang.Object>);
public <Z> Z fold(Z, scala.Function1<java.lang.Object, Z>);
public int value();
public SomeInt copy(int);
public int copy$default$1();
public java.lang.String productPrefix();
public int productArity();
public java.lang.Object productElement(int);
public scala.collection.Iterator<java.lang.Object> productIterator();
public boolean canEqual(java.lang.Object);
public int hashCode();
public java.lang.String toString();
public boolean equals(java.lang.Object);
public SomeInt(int);
}
:javap
has most of the same options as the javap
command-line tool, and you
javap -help
is useful for understanding what :javap
can do.
Scaladoc tips
The Scala standard library has extensive API documentation, but some useful features of the Scaladoc interface aren't immediately obvious.
Browsing symbolic operators
For example, suppose you see the symbol /:
in some Scala code. You
could try to figure out what type it's being called on and search for that type,
but you can also browse symbolic operators like this directly in the Scaladoc
interface. To do this, click the small #
in the upper left corner of the
screen.
Now you can search the page for /:
:
Instance and companion object definitions
Another simple but useful feature (which I personally only discovered after
writing Scala for an embarrassingly long time) is the ability to switch easily
between the docs for a class or trait and its companion object. If you're
reading the docs for the List
class, for example:
The circle with a "C" in it is a link that will take you to the docs for the
List
companion object:
There's no need to search for List
again to be able to select the object
instead of the class. Clicking the "O" will take you back to the class.