All Projects → travisbrown → scala-quickstart

travisbrown / scala-quickstart

Licence: other
Getting started in the Scala REPL

Programming Languages

shell
77523 projects
scala
5932 projects

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.

Operator list link

Now you can search the page for /::

Operator list

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:

Operator list link

The circle with a "C" in it is a link that will take you to the docs for the List companion object:

Operator list link

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.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].