This is a study note for Ammonite. It allows scripting in Scala and comes with an operating system library and a shell library.

1 Introduction

Ammonite is a mondern Scala REPL that has syntax highlighting, multi-line editing, library importing and other desired features missing in the default Scala REPL. It is lightweight without the project setting and loading of sbt. The Ammonite-REPL supports the following features

  • pretty printing using PPrint
  • editing with syntax highlighting, multi-liine editing, key-binding, history search, block input
  • importing scripts and Ivy dependencies
  • session saving and loading
  • auto completion
  • configuratble with ~/.ammonite/predef.sc
  • embeddable
  • os operations
  • shell operations such as pipe

Scala script let you save and run code without settings of a project. A script can import other scripts. Scripts may take arguments.

2 Ammonite REPL

Use brew install ammonite-repl to install Ammonite. Intialization code can be defined in ~/.ammonite/predef.sc.

2.1 Start and Quit

  • Run Ammonite: amm
  • Exit: exit or Ctrl-D.
  • Interrupt current execution: Ctrl-C
  • Use amm -w foo.sc to watch the file and rerun it when there is a change in the script. Use amm -w -p foo.sc to watch and open an REPL.
  • Configure truncation: one of two methods
    • use show command: show(cmd, height = numberOfLines)
    • customize pprinter: repl.pprinter() = repl.pprinter().copy(defaultHeight = 5)
  • Save/load sessions, session can be named
    • repl.sess.save()
    • repl.sess.load()

2.2 Editing

  • Auto completion: use tab
  • delete a word: Ctrl W
  • delete to the beginning: Ctrl U
  • moving cursor
    • start of the line: Ctrl A or Home
    • end of the line: Ctrl E or End
    • one word left/right: Esc-B/F or Opt-Left/Right
  • clear the screen: Ctrl L
  • Stop/Resume screen output: Ctrl-S/Q
  • Undo: Ctrl _
  • Redo: Opt/Esc _

2.3 Builtins

The bulitin imports include repl and uitilities.

The repl object has the following values:

  • prompt(): change prompt
  • frontEnd(): user input
  • help: help text
  • lastException: the last exception
  • sess: session
  • typeOf(t): find type of t
  • fullImports: show all imports
  • show(t: Any, width: Interger, height: Interger, indet: Integer): configure pretty-printing of a value

The interp object has the following values, some are imported in scope by default:

  • watch(p: os.Path): rerun when a file changes
  • load: load external scripts
  • repositories: resolvers
  • exit(value: Any): exit the REPL

The REPL has the following utilities:

  • source: peek source code
  • time {...}: time to run a command
  • browse: view large output
  • desugar: explain script code

2.4 Configuration

To config a repository

1
2
3
interp.repositories() ++= Seq(coursierapi.IvyRepository.of(
  "https://ambiata-oss.s3-ap-southeast-2.amazonaws.com/[defaultPattern]"
))
  • repl.prompt()
  • repl.frontEnd()
  • interp.colors()

Use interp.configureCompiler to configure Scala compiler.

These values are Ref[T]. It can be set with a static value using repl.prompt() = "abc" or to a live value (evulated everytime) as repl.prompt.bind(wd.toString + "@").

Jvm flags can be passed as environment variables when run amm, for example: JAVA_OPTS="-Xmx1024m" amm

3 Scripts

Amm introduces some special import syntax that the package name starts with $, including $file, $exec, and $ivy etc.

3.1 Import Scripts

To use block code, put multiple lines in a pair of curly braces {...}. To evaluate a code block, use double curly braces {{...}}.

Methods to import scripts

  • import $file.filePath: load a script file.
  • Use ^ for a parent folder.
  • Use import $file.filePath, filePath._ to load a script and its contents.
  • Rename import $file.{Foo => Bar}
  • Import mutiple scripts: import $file.{Foo, Bar}
  • Import $exec.filePath: import a script and dump its defintions

3.2 Import Libraries

To import Ivy libraries, usses import $ivy.id. The artifact is specified inside a pair of backticks.

1
2
// the `id` is a literal identifier
import $ivy.`org::library:version`, org.library._

The :: appends Scala major.minor version. The ::: appends the precision version of major.minor.pathcand cross-published suffixes. The : is used for full name, mostly Java libraries.

To load a compiler plugin, use import $plugin.$ivy.id. It doesn’t affect the run-time classpath.

Some scripts/libraries depend on runtime variables and need multi-stage importing. Use interp.load.module(script) or interp.load.ivy(id) to load, then import them.

3.3 Script Arguments

Define a main method to take commandline argument. The top-lvel definitions execute first, then the main method arguments.

Passing arugment by name using --, for example: --arg1 3.

It supports vararg*.

For mutlitple main methods, using @main decorator.

Use @doc("message") to document methods.

3.4 Bundled Libraries

Amm comes with Requests-scala for making HTTP calls, uPickle to deal with JSON data.

1
2
3
4
5
6
#!/usr/bin/env amm`

import $ivy.{
  `com.lihaoyi::requests:0.2.0`,
  `com.lihaoyi::ujson:0.7.5`
}

3.5 Script Predef and Run Script

The ~/.ammonite/predefScript.sc. is loaded before run a script form command line. You can link it with ~/.ammonite/predef.sc. if they have the same content.

Use amm scriptPath.sc to run a script file. To make it runnable from shell, add #!/usr/bin/env amm to the script file beginning and make it executable.

Use --watch/-w to re-run a script if it changes.

Use --predef/-p to run a script and open an interactive REPL to debug script.

3.6 Execution Model

Amm has a bytecode cache in ~/.ammonite/cache for compiled scripts. The script is wrapped in a package/object wrapper where the pakcage is the scirpt file path.

4 Ammonite Ops

4.1 Operations

Ammonite-Ops is a library that provides common OS operations in Scala. After import ammonite.ops._, you can use operations such as:

  • ls! path and ls.iter! path: return Vector[Path] or Iterator[Path]
  • ls.rec! path and ls.rec.iter! path: run recursively
  • read! path, read.lines! path, read.bytes! path
  • write(path, content), write.over(path, content) to overwrite an exsting file
  • rm! path: the same as recursive rm -rf
  • mv(src, dest)
  • cp(src, dest): the same as reursive cp -r src dest
  • exists! path
  • stat! path
  • ln(src, dest)
  • kill(9)! processId

4.2 File and Resource Paths

Amm has three path types:

  • Path: an absolute path
  • RelPath: a relative path
  • FilePath: a BasePath that could be Path or RelPath

A path can formed by a Path and strings/symbols seperated by a slash. For example pwd/folder/“folder 2”/“file.txt” or pwd/up. the up means one level up. Predefiend path values are pwd (a static value that is the folder where amm runs), root, home, wd (dynamic values changes with cd! command).

Use 'symbol or "string" for path segments.

Relative path are strings/symbols not started with a / and are seperated by slashes. A segement could be up or ... Use path1 relativeTo path2 to find the relative path between them. Use Path(pathStr, base) to use pathStr if it is an absolute string, otherwise, use the base path followed by pathStr. The base must be a value of Path.

The resource has a type of os.ResourcePath, it is the result of Thread.currentThread().getContextClassLoader appended with a /. You can only read from a resource path.

4.3 Extensions

Amm provides the following extensions:

  • things | f: things map f
  • things || f: things flatMap f
  • things |? f: things filter f
  • things |& f: things reduce f
  • things |! f: things foreach f
  • f! thing or thing |> f: f(thing)

4.4 Subprocess

To run a system command in a subprocess, use %cmd. It throws an InteractiveShelloutException if the retur code is non-zero. It is useful to run another program interactively using the current teminal. For example %vim or %python.

Use %%('cmd, "arg1", ,,,) for programmatic usage to return CommandResult. The shell command can be a path such as root/'bin/'bash. The CommandResult has the following fields:

  • exitCode field for exit code
  • out field for standard output. The out has methods .string, lines and .bytes.
  • err field for stand error

Yo can use symbol syntax as 'word for single word and use dobule quotes for mutiple words string such as "folder1/fodler2".

You can pass any environment variables as arguments in % or %%. For example: %%('bash, "-c", "echo \"Hello$ENV_ARG\"", ENV_ARG=12)

Use backticks to execute commands that aren’t valid Scala identifier. For example:

1
2
%`ssh-keygen`
%%("ssh-keygen)

5 Ammonite Shell

cd! path: change working directory wd: the current working directory

The Amm shell uses Scala and support any JVM code or libraries.