Release Maven Central


Scala REPL PlusPlus - a (slightly) better Scala 3 / dotty REPL. Note: this currently depends on a slightly patched version of dotty. I'll try to get those merged upstream.

Motivation: scala-repl-pp fills a gap between the standard Scala3 REPL, Ammonite and scala-cli.

Note: this currently depends on a dotty fork, which has since been merged into dotty upstream, i.e. we'll be able to depend on the regular dotty release from 3.2.2 on 🎉


Benefits over / comparison with

Regular Scala REPL

  • add runtime dependencies on startup with maven coordinates - automatically handles all downstream dependencies via coursier
  • pretty printing via pprint
  • customize greeting, prompt and shutdown code
  • multiple @main with named arguments (regular Scala REPL only allows an argument list)
  • predef code - i.e. run custom code before starting the REPL - via string and scripts
  • server mode: REPL runs embedded
  • easily embeddable into your own build


  • Ammonite's Scala 3 support is far from complete - e.g. autocompletion for extension methods has many shortcomings. In comparison: scala-repl-pp uses the regular Scala3/dotty ReplDriver.
  • Ammonite has some Scala2 dependencies intermixed, leading to downstream build problems like this. It's no longer easy to embed Ammonite into your own build.
  • Note: Ammonite allows to add dependencies dynamically even in the middle of the REPL session - that's not supported by scala-repl-pp yet. You need to know which dependencies you want on startup.


scala-cli is mostly a wrapper around the regular Scala REPL and Ammonite, so depending on which one you choose, you essentially end up with the same differences as above.

Build it locally

Prerequisite for all of the below:

sbt stage

Generally speaking, --help is your friend!

./scala-repl-pp --help


# run with defaults

# customize prompt, greeting and exit code
./scala-repl-pp --prompt=myprompt --greeting='hey there!' --onExitCode='println("see ya!")'

# pass some predef code
./scala-repl-pp --predefCode='def foo = 42'
scala> foo
val res0: Int = 42

Add dependencies with maven coordinates

Note: the dependency must be known at startup time, either via --dependency parameter...

./scala-repl-pp --dependency com.michaelpollmeier:versionsort:1.0.7
scala>"1.0", "0.9")
val res0: Int = 1

... or using lib directive in predef code or predef files...

echo '//> using lib com.michaelpollmeier:versionsort:1.0.7' >


scala>"1.0", "0.9")
val res0: Int = 1

Importing additional script files interactively

echo 'val bar = foo' >


val foo = 1
//> using file
println(bar) //1


See ScriptRunnerTest for a more complete and in-depth overview.

Simple "Hello world" script

./scala-repl-pp --script

Predef code for script

./scala-repl-pp --script --predefCode 'val foo = "Hello, predef!"'

Predef code via environment variable

export SCALA_REPL_PP_PREDEF_CODE='val foo = "Hello, predef!"'
./scala-repl-pp --script

Predef file(s)


val foo = "Hello, predef file"
./scala-repl-pp --script --timesiles

Importing files / scripts

val foo = 42

//> using file
./scala-repl-pp --script


Dependencies can be added via //> using lib syntax (like in scala-cli).

//> using lib com.michaelpollmeier:versionsort:1.0.7

val compareResult ="1.0", "0.9")
assert(compareResult == 1,
       s"result of comparison should be `1`, but was `$compareResult`")
./scala-repl-pp --script

Note: this also works with using directives in your predef code - for script and REPL mode.

@main entrypoints

@main def main() = println("Hello, world!")
./scala-repl-pp --script

multiple @main entrypoints:

@main def foo() = println("foo!")
@main def bar() = println("bar!")
./scala-repl-pp --script --command=foo

named parameters

@main def main(name: String) = {
  println(s"Hello, $name!")
./scala-repl-pp --script --params name=Michael

Server mode

./scala-repl-pp --server

curl http://localhost:8080/query-sync -X POST -d '{"query": "val foo = 42"}'
curl http://localhost:8080/query-sync -X POST -d '{"query": "val bar = foo + 1"}'

Embed into your own project

Try out the working string calculator example in this repo:

cd src/test/resources/demo-project
sbt stage

Welcome to the magical world of string calculation!
Type `help` for help

stringcalc> add(One, Two)
val res0: stringcalc.Number = Number(3)

Predef code and scripts

There's a variety of ways to define predef code, i.e. code that is being run before any given script:

echo 'def bar = 90' > ~/
echo 'def baz = 91' >
echo 'def bam = 92' >
export SCALA_REPL_PP_PREDEF_CODE='def bax = 93'

./scala-repl-pp --predefCode='def foo = 42',

scala> foo
val res0: Int = 42

scala> bar
val res1: Int = 90

scala> baz
val res2: Int = 91

scala> bam
val res3: Int = 92

scala> bax
val res4: Int = 93


Why are script line numbers incorrect?

Scala-REPL-PP currently uses a simplistic model for predef code|files and additionally imported files, and just copies everything into one large script. That simplicity naturally comes with a few limitations, e.g. line numbers may be different from the input script(s).

A better approach would be to work with a separate compiler phase, similar to what Ammonite does. That way, we could inject all previously defined values|imports|... into the compiler, and extract all results from the compiler context. That's a goal for the future.

If there's a compilation issue, the temporary script file will not be deleted and the error output will tell you it's path, in order to help with debugging.