Ammonite API
The Ammonite API consists in instances of several classes, that are created by Ammonite or almond, and allow you to interact with the REPL. These instances are accessible either by their name, from the REPL, like interp, or via implicits, like implicitly[ammonite.interp.api.InterpAPI].
The following instances are available:
interp, which is aammonite.interp.api.InterpAPI,repl, which is aammonite.repl.api.ReplAPI.
InterpAPI
InterpAPI allows to
- load new dependencies or compiler plugins,
- add repositories for dependencies,
- add exit hooks,
- configure compiler options.
Load dependencies
interp.load.ivy accepts one or several coursier.Dependency.
Load simple dependencies
interp.load.ivy("org.platanios" %% "tensorflow-data" % "0.4.1")
Load dependencies while adjusting some parameters
interp.load.ivy(
// replace with linux-gpu-x86_64 on linux with nvidia gpu or with darwin-cpu-x86_64 on macOS
("org.platanios" %% "tensorflow" % "0.4.1").withClassifier("linux-cpu-x86_64")
)
The dependencies can then be used in the cell right after the one calling interp.load.ivy.
Note that in the case of simple dependencies, when directly entering code in a notebook, the following syntax is preferred and allows to use the dependency in the current cell rather than the next one:
import $ivy.`org.platanios::tensorflow-data:0.4.1`
The same goes for the //> using dep directive:
//> using dep "org.platanios::tensorflow-data:0.4.1"
Both syntaxes accept a classifier, appended to the dependency as ,classifier=…:
import $ivy.`org.platanios::tensorflow:0.4.1,classifier=linux-cpu-x86_64`
//> using dep "org.platanios::tensorflow:0.4.1,classifier=linux-cpu-x86_64"
Pinning dependency versions
Loading dependencies upfront in a first cell effectively "pins" their versions: dependencies loaded in later cells cannot replace them, even if they request newer versions transitively. Run this first cell before loading libraries that depend on those dependencies.
For example, the workaround in issue #332 loads Hadoop before Spark. In a first cell:
import $ivy.`org.apache.hadoop:hadoop-common:2.9.2`
import $ivy.`org.apache.hadoop:hadoop-azure-datalake:3.1.1`
Then, in a separate cell:
import $ivy.`org.apache.spark::spark-sql:2.4.0`
This is both a feature and a limitation: it lets you keep chosen versions when loading more libraries, but also prevents upgrading an already loaded dependency in a later cell. To change those versions, restart the kernel and run the updated dependency cell first. The chosen versions still need to be compatible with the libraries that use them.
Load compiler plugins
interp.load.plugin.ivy accepts one or several coursier.Dependency.
interp.load.plugin.ivy("org.spire-math" %% "kind-projector" % "0.9.9")
The plugins can then be used in the cell right after the one calling interp.load.plugin.ivy.
Note that when directly entering code in a notebook, the following syntax is more concise, and allows to use the compiler plugin in the current cell:
import $plugin.$ivy.`org.spire-math::kind-projector:0.9.9`
// example of use
trait T[F[_]]
type T2 = T[Either[String, ?]]
Load scripts
Ammonite scripts can be loaded from notebooks, with the same syntax as in Ammonite:
import $file.path.to.script
This compiles and runs path/to/script.sc, and brings its wrapper object in scope, so that its definitions can be accessed like script.foo(). Paths are relative to the working directory of the kernel, which is usually the directory of the notebook, and ^ stands for the parent directory.
Alternatively, scripts can be loaded with a directive, which accepts any path:
//> using script path/to/script.sc
//> using scripts path/to/script.sc, /absolute/path/to/other.sc
The wrapper object of each script is brought in scope under the name of the script file (script for path/to/script.sc), like import $file.… does.
Either way, a script is compiled and run only once in a session, unless its content changed since it was last loaded. In that case, importing it or loading it again re-compiles and re-runs it, and cells run after that see its new definitions.
Add repositories
One can add extra coursier.Repository via
import coursierapi._
interp.repositories() ++= Seq(
MavenRepository.of("https://nexus.corp.com/content/repositories/releases")
.withCredentials(Credentials.of("user", "pass"))
)
Add exit hooks
interp.beforeExitHooks += { _ =>
// called before the kernel exits
}
Configure compiler options
interp.preConfigureCompiler accepts a function that updates the settings of the compiler, and requests a fresh compiler instance, used from the next cell onwards. In Scala 2, it receives a scala.tools.nsc.Settings:
// Scala 2 - fail on warnings
interp.preConfigureCompiler(_.fatalWarnings.value = true)
In Scala 3, it receives a dotty.tools.dotc.core.Contexts.FreshContext:
// Scala 3 - fail on warnings
interp.preConfigureCompiler { ctx =>
ctx.setSetting(ctx.settings.XfatalWarnings, true)
}
See Compiler options for more details, including how to pass options as strings, and how to set them with //> using option directives rather than via this API.
ReplAPI
ReplAPI allows to
- access the pretty-printer and customize its behavior,
- access the latest thrown exception,
- access the command history,
- request the compiler instance to be re-created,
- get the current compiler instance,
- get the current imports,
- evaluate code from strings or files, and lastly
- get the class names and byte code of the code entered during the current session.
Pretty-printer
class Foo(val x: Int)
repl.pprinter() = {
val p = repl.pprinter()
p.copy(
additionalHandlers = p.additionalHandlers.orElse {
case f: Foo =>
pprint.Tree.Lazy(_ => Iterator(fansi.Color.Yellow(s"foo: ${f.x}").render))
}
)
}
Latest thrown exception
repl.lastException // last thrown exception, or null if none were thrown
Command history
repl.history // current session history
repl.fullHistory // shared history
Refresh compiler instance
repl.newCompiler()
Get compiler instance
repl.compiler // has type scala.tools.nsc.Global
Get current imports
repl.imports
Evaluate code
repl.load("val a = 2")
Byte code of REPL inputs
repl.sess.frames.flatMap(_.classloader.newFileDict).toMap
// Map[String, Array[Byte]], keys: class names, values: byte code