REPL, Worksheets & Scastie

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Scala 3.10 (3.10.0, released 2026-10-12) with the Scala CLI 1.17.1 runner that ships in that distribution, and every REPL transcript was recorded on JDK 26 on 2026-10-15. Features newer than the 3.3 LTS name their release, and the page says where they are missing from the 3.9 LTS line. There are three low-ceremony ways to run Scala without creating a build: the REPL for one-line experiments, a worksheet for a file that shows the value of every expression next to it, and Scastie for a playground that needs nothing installed. Installing the tools is covered on Installing Scala, complete programs with an entry point on Hello World & @main Methods, and the full Scala CLI command set on Scala CLI. IDE setup is on IDEs. The Java counterpart of the REPL is JShell, described on Getting Started with Java, and the Groovy counterpart on groovysh & groovyconsole.

Starting the REPL

The REPL (read-eval-print loop) reads one input, compiles it, runs it and prints the result. In the Scala 3.10.0 distribution the scala command is the Scala CLI runner, so the REPL is a sub-command of it. The version check shows which runner and which Scala version a bare scala command uses:

scala version
scala repl
Scala code runner version: 1.17.1
Scala version (default): 3.10.0
Welcome to Scala 3.10.0 (26, Java Java HotSpot(TM) 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.

scala>

Pass -S 3.10.0 (long form --scala-version) to pin the Scala version of a session. The REPL is built into every distribution, so nothing else needs installing, and :quit (alias :exit) or Ctrl+D leaves it. A stand-alone scala-cli repl command starts the same REPL when the Scala CLI launcher is installed separately. Inside a session each input is compiled on its own against the definitions of the earlier ones, so a value defined on one line is visible on the next.

What the REPL prints

Every input that produces a value is printed as val name: Type = value. An expression that is not assigned gets a generated name, res0, res1 and so on, that later inputs can use. Definitions of types are echoed as a comment, and def prints its signature. The next transcript uses the trailhead scenario from the landing page: an enum for the difficulty of a trail and a case class for the trail itself.

scala> enum Difficulty:
  case Easy, Moderate, Hard
  case Alpine(grade: Int)

// defined class Difficulty

scala> case class Trail(name: String, lengthKm: Double, ascentM: Int, difficulty: Difficulty)
// defined case class Trail

scala> val pico = Trail("Camino al Pico", 12.5, 850, Difficulty.Alpine(3))
val pico: Trail = Trail(
  name = "Camino al Pico",
  lengthKm = 12.5,
  ascentM = 850,
  difficulty = Alpine(3)
)

scala> pico.lengthKm * 2
val res0: Double = 25.0

scala> def describe(km: Double): String =
  if km < 5 then "short"
  else if km < 10 then "medium"
  else "long"

def describe(km: Double): String

scala> describe(res0)
val res1: String = "long"

Two details are worth noticing. A block that continues over several lines, such as the enum or the def, ends at the first blank line, and braceless syntax needs no braces to tell the REPL the input is incomplete. And the case class is printed with its field names and wrapped over several lines: that is the pretty printer.

The pretty printer (3.8)

Before 3.8 the REPL printer dumped a long collection or a deeply nested value as a single line that was hard to read. Scala 3.8 renders results with the fansi and pprint libraries instead: nested case classes are indented, named fields are shown, strings are quoted, and values that fit within the line width stay on one line.

scala> val trails = List(pico, Trail("Lago", 4.2, 120, Difficulty.Easy), Trail("Cresta", 9.8, 640, Difficulty.Hard))
val trails: List[Trail] = List(
  Trail(
    name = "Camino al Pico",
    lengthKm = 12.5,
    ascentM = 850,
    difficulty = Alpine(3)
  ),
  Trail(name = "Lago", lengthKm = 4.2, ascentM = 120, difficulty = Easy),
  Trail(name = "Cresta", lengthKm = 9.8, ascentM = 640, difficulty = Hard)
)

scala> trails.map(_.name)
val res2: List[String] = List("Camino al Pico", "Lago", "Cresta")

The printer also truncates very long collections. (1 to 60).toList prints 48 elements, one per line, followed by a line containing only ...:

scala> (1 to 60).toList
val res0: List[Int] = List(
  1,
  2,
  3,
  (lines 4 to 46 omitted here)
  47,
  48,
...

The 3.10 release notes also list a -Yrepl-print-tostring option that prints results with toString instead of the pretty printer. Passing it to 3.10.0 on the command line or with :settings printed bad option '-Yrepl-print-tostring' was ignored in the session recorded for this page, and the output stayed pretty-printed, so check it against the compiler version you use before relying on it. Source: Scala 3.8 release notes, Scala 3.10 release notes

REPL commands

Lines that start with a colon are commands for the REPL itself and are not Scala code. :help lists them. The table reproduces what 3.10.0 prints.

Command What it does

:help

Prints the command summary and the supported directives.

:save <path>

Saves a replayable session to a file (3.10).

:load <path>

Interprets the lines of a file as REPL input.

:quit

Leaves the REPL. :exit is an alias.

:type <expression>

Prints the type of an expression without evaluating its value.

:doc <expression>

Prints the documentation of the given expression.

:imports

Shows the import history.

:reset [options]

Clears the session and starts fresh, optionally with new compiler options.

:replay [options]

Resets, then re-runs the session, optionally with new compiler options (3.10).

:settings <options>

Updates compiler options, if possible.

:silent

Turns the automatic printing of results off and on.

:jar <path>

Adds a JAR to the classpath (3.7).

:dep <group>::<artifact>:<version>

Resolves a dependency and makes it available.

:toolkit <version>|default|<flavor>:<version>

Resolves a Scala Toolkit and makes it available.

:type is the command used most often, because it answers "what did the compiler infer?" without a val to hold the answer:

scala> :type trails.map(_.ascentM).sum
Int

scala> :type trails.groupBy(_.difficulty)
Map[Difficulty, List[Trail]]

:silent toggles printing, which helps when a definition produces a huge value. In the recorded session val a = 10 printed nothing while silent mode was on, and the next :silent switched printing back on. :imports lists what the session has imported implicitly; on 3.10.0 that is the pretty printer’s pprintln, plus any import you have typed. :doc printed No doc for the expressions tried in this session (List(1, 2).map, Option and "abc".length), so treat it as available but not always helpful; the Scaladoc described on Scaladoc is the dependable route to API documentation. The :dep and :toolkit commands download artifacts, so they were not run here.

Errors and warnings

Compiler messages appear exactly as in a compilation, with their error codes. Deprecation and feature warnings are on by default in the 3.10 REPL, which makes it a good place to try a call before using it in a project:

scala> val x: Int = "hello"
-- [E007] Type Mismatch Error: -------------------------------------------------
1 |val x: Int = "hello"
  |             ^^^^^^^
  |             Found:    ("hello" : String)
  |             Required: Int
  |
  | longer explanation available when compiling with `-explain`
1 error found

scala> pico.lengthKm + "km"
1 warning found
-- Deprecation Warning: --------------------------------------------------------
1 |pico.lengthKm + "km"
  |^^^^^^^^^^^^^^^
  |method + in class Double is deprecated since 2.13.0: Adding a number and a String is deprecated. Use the string interpolation `s"$num$str"`
val res4: String = "12.5km"

The message offers -explain. Compiler options can be changed in a running session with :settings:

scala> :settings -explain

scala> val y: Int = "x"
-- [E007] Type Mismatch Error: -------------------------------------------------
1 |val y: Int = "x"
  |             ^^^
  |             Found:    ("x" : String)
  |             Required: Int
  |-----------------------------------------------------------------------------
  | Explanation (enabled by `-explain`)
  |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  |
  | Tree:
  |
  | "x"
  |
  | I tried to show that
  |   ("x" : String)
  | conforms to
  |   Int
  | but none of the attempts shown below succeeded:
  |
  |   ==> ("x" : String)  <:  Int
  |     ==> String  <:  Int  = false
  |
  | The tests were made under the empty constraint
   -----------------------------------------------------------------------------
1 error found

The list of options is on Compiler Options, Linting & Errors.

Companions need one input

Because every input is compiled separately, a class and an object with the same name that are entered one after the other are not companions of each other, and the object cannot reach the class’s private constructor:

scala> class Hut private (val beds: Int)
// defined class Hut

scala> object Hut:
  def apply(beds: Int) = new Hut(beds)

-- [E173] Reference Error: -----------------------------------------------------
2 |  def apply(beds: Int) = new Hut(beds)
  |                             ^^^
  |    constructor Hut cannot be accessed as a member of Hut from object Hut.
  |      private constructor Hut can only be accessed from class Hut.
1 error found

Put both definitions in a file and load them together with :load. A file hut.sc with the class, the object and a final expression:

class Hut private (val beds: Int)
object Hut:
  def apply(beds: Int) = new Hut(beds)

Hut(12).beds
scala> :load hut.sc
// defined class Hut
// defined object Hut
val res0: Int = 12

This is the Scala 3 replacement for the :paste command of the Scala 2 REPL. :paste, :power and :javap are not in the 3.10.0 command list above. Companions are explained on Objects & Companions.

Saving, loading and replaying a session

:save <path> writes the inputs of the session to a file, and :load <path> runs a file as if its lines had been typed. Scala 3.10 added :save and :replay: :replay resets the REPL and re-runs the saved inputs, and both accept new compiler options, which is how a session can be re-run with, say, -explain or a different -source setting. Neither command exists on the 3.9 LTS line, so a session recorded with 3.10 is not portable to an older compiler’s REPL by :replay. A session that mixes definitions and expressions:

scala> case class Trail(name: String, lengthKm: Double)
// defined case class Trail

scala> val lago = Trail("Lago", 4.2)
val lago: Trail = Trail(name = "Lago", lengthKm = 4.2)

scala> lago.lengthKm * 2
val res0: Double = 8.4

scala> :save hike.sc

scala> :reset
Resetting REPL state.

scala> :load hike.sc
// defined case class Trail
val lago: Trail = Trail(name = "Lago", lengthKm = 4.2)
val res0: Double = 8.4

scala> :replay
Replaying REPL session.
Resetting REPL state.
// defined case class Trail
val lago: Trail = Trail(name = "Lago", lengthKm = 4.2)
val res0: Double = 8.4

The saved file is plain text with a comment before each entry, so it can be edited and loaded again:

/* Scala REPL session */
/* ---- entry ---- */
case class Trail(name: String, lengthKm: Double)
/* ---- entry ---- */
val lago = Trail("Lago", 4.2)
/* ---- entry ---- */
lago.lengthKm * 2

Commands typed in the session are saved too. In a session where :silent had been typed, hike.sc contained a :silent line, and loading it toggled the setting again, so review a saved file before sharing it.

Using libraries in the REPL

There are four ways to put code that is not in the standard library on the REPL’s classpath. All start from the same trailhead helper, compiled into a JAR with Scala CLI. The source file Naismith.scala implements Naismith’s rule (12 minutes per kilometre plus 10 minutes per 100 m of ascent):

//> using scala 3.10.0
package trailhead

object Naismith:
  /** Walking time in minutes: 12 minutes per km plus 10 minutes per 100 m of ascent. */
  def minutes(lengthKm: Double, ascentM: Int): Int =
    (lengthKm * 12 + ascentM / 100.0 * 10).round.toInt
scala compile Naismith.scala -d trailhead.jar
Compiling project (Scala 3.10.0, JVM (26))
Compiled project (Scala 3.10.0, JVM (26))

:jar (3.7). Scala 3.7 added :jar, which adds a JAR to a session that is already running. The 3.7 release notes call it the equivalent of Scala 2’s :require. It also works on the 3.9 LTS line.

scala> :jar trailhead.jar
Added 'trailhead.jar' to classpath.

scala> trailhead.Naismith.minutes(12.5, 850)
val res0: Int = 235

//> using directives (3.10). Scala 3.10 lets a session start with the same directives that Scala CLI reads from a source file. The release notes name dep, toolkit and jar, on their own or followed by code in the same input, and say not to mix them with : commands in a single input. The jar directive was run here:

scala> //> using jar trailhead.jar
Added 'trailhead.jar' to classpath.

scala> trailhead.Naismith.minutes(4.2, 120)
val res0: Int = 62

If the JAR is already on the classpath, for instance after :jar, the directive is refused with The path 'trailhead.jar' cannot be loaded, it contains a classfile that already exists on the classpath. Other directives are not supported inside a session; they print a warning instead of being applied:

scala> //> using option -Wunused:all
[warn] The `using option` directive is not supported in the REPL.
To use it, re-run with the `scala` command and pass the directive inside an input.

scala> //> using scala 3.9.0
[warn] The `using scala` directive is not supported in the REPL.
To use it, re-run with the `scala` command and pass the directive inside an input.

The dep and toolkit directives, and the :dep and :toolkit commands, resolve artifacts from Maven Central. They were not run here, because this page’s examples were recorded without downloading anything new. The forms below follow the :help output of 3.10.0 and the 3.10 release notes (not run here):

scala> //> using toolkit default
     | os.pwd

default stands for the Scala Toolkit version known to the runner, 0.9.2 for the Scala CLI 1.17.1 used here. The Toolkit’s libraries start on Files & Processes with OS-Lib. A single dependency is written //> using dep followed by group::artifact:version, with :: selecting the artifact built for the Scala binary version.

--jar and sources on the command line. Scala CLI options add the same things before the session starts, and a source file passed to scala repl is compiled first and exposed to the session, as the Scala CLI documentation describes:

scala repl Naismith.scala
scala> import trailhead.Naismith

scala> Naismith.minutes(9.8, 640)
val res0: Int = 182

--toolkit and --dep (not run here). scala repl --toolkit default and scala repl --dep <group>::<artifact>:<version> start a session with the Toolkit or a dependency already on the classpath.

Init scripts, watch mode and JShell

The REPL can run a script before it hands over the prompt. --repl-init-script takes the code as a string, --repl-init-script-file takes it from a file, and --repl-quit-after-init leaves as soon as the script has run. Together they run a snippet inside the REPL’s own environment rather than through the normal runner, which is mainly useful to investigate REPL behaviour:

scala repl --repl-init-script 'import trailhead.Naismith; println(Naismith.minutes(9.8, 640))' --jar trailhead.jar --repl-quit-after-init
182

The Scala CLI page also documents --watch, which recompiles the inputs and restarts the session when a source file changes, and passing REPL options after a -- separator or with -O. Those two were not run here.

JShell (3.9). The Scala CLI that ships with Scala 3.9 can start JShell instead of the Scala REPL, so a project’s classpath can be explored from Java. It is the default for pure Java projects and is marked experimental in the Scala CLI documentation:

scala repl --jshell --jar trailhead.jar
|  Welcome to JShell -- Version 26
|  For an introduction type: /help intro

jshell> import trailhead.Naismith;

jshell> Naismith.minutes(12.5, 850)
$2 ==> 235

jshell> /exit
|  Goodbye

The Scala object is called from Java syntax through its static forwarder. See Calling Scala from Java.

Worksheets

A worksheet is a Scala file that is evaluated when it is saved, with the value of each top-level expression shown next to the code. It is a REPL session kept in a file, so it can be edited, versioned and re-run. The Scala 3 Book says worksheets use the extension .worksheet.sc, that they have no program entry point, and that top-level statements and expressions are evaluated from top to bottom. A worksheet for the trailhead scenario:

//> using scala 3.10.0

case class Trail(name: String, lengthKm: Double, ascentM: Int)

val trails = List(Trail("Lago", 4.2, 120), Trail("Pico", 12.5, 850))

trails.map(_.ascentM).sum
trails.maxBy(_.lengthKm).name
println(s"${trails.size} trails")

Opened in an editor with worksheet support, the two bare expressions show 970 and "Pico" beside their lines, and the println output appears with them; that rendering depends on the editor and was not run here.

Metals (VS Code and other editors)

Metals treats a file as a worksheet only when its name ends in .worksheet.sc; an ordinary .sc file is a Scala script. Worksheets are powered by mdoc. Where the file is created matters: inside the sources of the project, next to the existing Scala files, it can use the project’s classes and values, while elsewhere it sees only the standard library and its dependencies. After saving, each result appears as an inlay hint at the end of the line, and long results can be expanded by hovering over the hint. Nothing has to be wrapped in an object. External dependencies and compiler options are added with //> using dep and //> using options lines at the top, for example //> using dep com.lihaoyi::scalatags:0.7.0 in the Metals documentation. One limitation the documentation notes: a value class (a class that extends AnyVal) defined in a worksheet fails with the mdoc error value classes may not be a member of another class, and the fix is to move it to a separate file. The Scala 3 Book describes creating one with the Metals: New Scala project command and a file named hello.worksheet.sc under src/main/scala/, and saving the file to evaluate it.

IntelliJ IDEA

The IntelliJ Scala plugin evaluates worksheets too. According to the Scala 3 Book, a worksheet in IntelliJ is created from the context menu of src/main/scala/ as a new file called hello.worksheet.sc, is evaluated with the green arrow at the top of the editor, and shows the result of every line in a panel on the right. The worksheet uses the Scala version defined by the project, normally the scalaVersion key of build.sbt. IDE setup is on IDEs.

Worksheets versus scripts in Scala CLI

Scala CLI also runs .sc files, but as scripts: the top-level statements are run and only what the program prints appears. Values of bare expressions are not shown, which is the main difference from a worksheet. Running the worksheet above prints only the println line, and Scala CLI warns about the file name, because the dot in hike.worksheet.sc becomes a reserved $u002E sequence in the generated object name:

scala run hike.worksheet.sc
Compiling project (Scala 3.10.0, JVM (26))
[warn] ./.scala-build/repl-worksheets-and-scastie_f496761b3b-60c4d35074/src_generated/main/hike.worksheet.scala:20:8
[warn] The identifier `hike$u002Eworksheet_sc` should not contain `$`, which is reserved for internal compiler use.
[warn] object hike$u002Eworksheet_sc {
[warn]        ^^^^^^^^^^^^^^^^^^^^^^
Compiled project (Scala 3.10.0, JVM (26))
2 trails

Rename the file to hike.sc when it is meant to be run from the command line, and keep .worksheet.sc for files that live in an editor. A one-liner can also be run without a file: scala -e 'println(List(4.2, 12.5).sum)' printed 16.7 on the recorded machine. Scripts and entry points are covered on Hello World & @main Methods.

Scastie

Scastie is an online playground that runs Scala programs in the browser, with nothing to install. According to the project’s README it is "sbt in your browser": the code, together with the chosen build settings, is sent to a server, run in an isolated JVM, and the output is streamed back, so do not paste anything confidential into it. Its documented capabilities are:

  • any Scala version, and alternative backends: Scala 3, Scala.js, Scala Native and Typelevel Scala;

  • any publicly available library, found through a Scaladex search box instead of typing coordinates and versions;

  • sharing a program with a link, and embedding saved snippets in a web page or a library’s user guide with a script tag;

  • a worksheet mode that works like an IDE worksheet: top-level expressions without a class or main, with the value and type of each shown beside the code, and interleaved HTML blocks for literate programming.

The Scala 3 Book points to Scastie as the browser-based alternative to the REPL. A snippet to paste into worksheet mode, which was verified in the REPL transcripts above but not run on Scastie itself:

enum Difficulty:
  case Easy, Moderate, Hard
  case Alpine(grade: Int)

case class Trail(name: String, lengthKm: Double, difficulty: Difficulty)

val trails = List(
  Trail("Lago", 4.2, Difficulty.Easy),
  Trail("Pico", 12.5, Difficulty.Alpine(3)))

trails.groupBy(_.difficulty).view.mapValues(_.size).toMap

Scastie is the quickest way to share a reproducible example in a question or a bug report, because the link carries the code, the Scala version and the dependencies.

Choosing between them

Tool Best for Needs Keeps a record

REPL (scala repl)

A quick type check, trying an API, loading a JAR or a source file and poking at it

The scala command

:save and :load (:save since 3.10)

Worksheet (.worksheet.sc)

Longer experiments where every line’s value should stay visible next to the code

Metals or the IntelliJ Scala plugin

The file itself

Script (.sc with Scala CLI)

Small programs that print their own output

The scala command

The file itself

Scastie

Sharing or trying code on a machine with no Scala installed

A browser

A shareable link

Where the books are outdated

The Scala 3 Book’s page on the REPL is brief and still accurate for the basics: res0-style names, def echoing and the pointer to Scastie and worksheets. It does not cover what changed from 3.7 to 3.10:

  • The pretty-printed output, with field names and line wrapping, arrived in 3.8. Older transcripts show toString output on one line.

  • :jar arrived in 3.7, and :save, :replay and //> using directives in the session arrived in 3.10.

  • Since 3.8 the REPL is a separate scala3-repl artifact. Tools that embed it need an explicit dependency, and the 3.8 release notes list minimum and recommended build-tool versions (sbt 1.12.0 and Scala CLI 1.11.0 were recommended) for anything that starts the REPL.

  • The overview page under the "overviews" section of the documentation describes the Scala 2 REPL and says so at the top. Its :paste, :power, :javap and :require have no entry in the Scala 3 :help, and :jar replaces :require.

  • Material written for Scala 2 starts the REPL with a different launcher, while the 3.10.0 distribution’s scala command is the Scala CLI runner, which accepts the extra options shown above.

  • JShell as a REPL backend is newer still: it needs the Scala CLI that comes with 3.9.