Clojure

The REPL and main entry points

The clojure.main namespace

The clojure.main namespace provides functions that allow Clojure programs and interactive sessions to be launched via Java’s application launcher tool java.

clojure.main --help

The clojure.main/main entry point accepts a variety of arguments and flags.

  • With no options or args, runs an interactive Read-Eval-Print Loop

  • init options:

    • -i, --init path Load a file or resource

    • -e, --eval string Evaluate expressions in string; print non-nil values

    • --report target Report uncaught exception to "file" (default), "stderr", or "none", overrides System property clojure.main.report (added in 1.10.1)

  • main options:

    • -r, --repl Run a repl

    • path Run a script from a file or resource

    • - Run a script from standard input

    • -m, --main A namespace to find a -main function for execution

    • -h, -?, --help Print this help message and exit

  • operation:

    • Establishes thread-local bindings for commonly set!-able vars

    • Enters the user namespace

    • Binds *command-line-args* to a seq of strings containing command line args that appear after any main option

    • Runs all init options in order

    • Runs a repl or script if requested

The init options may be repeated and mixed freely, but must appear before any main option. The appearance of any eval option before running a repl suppresses the usual repl greeting message: "Clojure ~(clojure-version)".

Paths may be absolute or relative in the filesystem or relative to classpath. Classpath-relative paths have prefix of @ or @/

Launching a REPL

The simplest way to launch a Clojure repl is to use the clj command tool, which invokes clojure.main:

$ clj
Clojure 1.12.0
user=>

The REPL prompt shows the name of the current namespace (*ns*), which defaults to user.

Several special vars are available when using the REPL:

  • *1, *2, *3 - hold the result of the last three expressions that were evaluated

  • *e - holds the result of the last exception.

The clojure.repl namespace has a number of useful functions for inspecting the source and documentation of available functions:

  • doc - prints the docstring for a var given its name

  • find-doc - prints the docstring for any var whose doc or name matches the pattern

  • apropos - returns a seq of definitions matching a regex

  • source - prints the source for a symbol

  • pst - print stack trace for a given exception or *e by default

Launching a Script

To run a file full of Clojure code as a script, pass the path to the script to clojure.main as an argument:

clj -M /path/to/myscript.clj

Passing arguments to a Script

To pass in arguments to a script, pass them in as further arguments when launching clojure.main:

clj -M /path/to/myscript.clj arg1 arg2 arg3

The arguments will be provided to your program as a seq of strings bound to the var *command-line-args*:

*command-line-args* => ("arg1" "arg2" "arg3")

Error printing

At REPL

As of Clojure 1.10, Clojure errors categorized into one of several phases:

  • :read-source - an error thrown while reading characters at the REPL or from a source file.

  • :macro-syntax-check - a syntax error found in the syntax of a macro call, either from spec or from a macro throwing IllegalArgumentException, IllegalStateException, or ExceptionInfo.

  • :macroexpansion - all other errors thrown during macro evaluation are categorized as macroexpansion errors.

  • :compile-syntax-check - a syntax error caught during compilation.

  • :compilation - non-syntax errors caught during compilation.

  • :execution - any errors thrown at execution time.

  • :read-eval-result - any error thrown while reading the result of execution (only applicable for REPLs that read the result).

  • :print-eval-result - any error thrown while printing the result of execution.

Exceptions thrown during all phases (exception :execution) will have ex-data attached with one or more of the following keys:

  • :clojure.error/phase - phase indicator

  • :clojure.error/source - file name (no path)

  • :clojure.error/line - integer line number

  • :clojure.error/column - integer column number

  • :clojure.error/symbol - symbol being expanded/compiled/invoked

  • :clojure.error/class - cause exception class symbol

  • :clojure.error/cause - cause exception message

  • :clojure.error/spec - explain-data for a spec error

The clojure.main REPL includes the categorization and printing of errors by default, but the individual steps of this process are exposed as well for other REPLs to use, specifically the functions:

  • Throwable->map - converts an Exception chain into Clojure data

  • ex-triage - analyzes Clojure exception data to pull relevant information from the top and bottom of the exception chain into a map describing just the set of data needed to format an exception string

  • ex-str - produces a phase-appropriate message given a set of exception data

The clojure.main REPL combines these functions in a pipeline to produce the printed exception message: (-> ex Throwable->map clojure.main/ex-triage clojure.main/ex-str). Other REPLs can use one or more pieces of this pipeline as necessary when building or customizing their exception printing.

As launcher

Up to Clojure 1.10.0, clojure.main when used as a program launcher (with -m, -e, or with a script), uncaught exceptions would be automatically printed along with the full nested stack trace. In this case, the error triage and printing process above was not applied.

As of Clojure 1.10.1, uncaught exceptions will now be caught and printed according to the same error triage and printing functionality as the Clojure REPL. The full stack trace, ex-info, and other information will be printed to a target specified by the configuration.

The three available error targets are:

  • file - write to a temp file (default, falls back to stderr)

  • stderr - write to stderr stream

  • none - don’t write

These error targets can be specified either as options to clojure.main, or as Java system properties (flags take precedence). When invoking clojure.main (or using the clj tool), use --report <target>. For Java system property, use -Dclojure.main.report=<target>.

Other programs may wish to take advantage of this functionality, and it is available in report-error, which takes a Throwable and optionally the :target.

The user namespace

By default, the Clojure REPL starts in the user namespace and this namespace is typically used for exploratory work.

The Clojure REPL automatically loads the following namespaces and refers the following functions:

If you switch to a different namespace (with in-ns or ns), these functions will not be available unless referred there explicitly.

Loading of user.clj

The Clojure runtime will look for and load user.clj on runtime startup, if it is found on the classpath. This is a facility designed to provide development-time facilities, and generally not recommended in production use.

Because the user.clj file is loaded by the Clojure runtime on initialization, this typically happens before the main namespace in an application executes. Any namespaces or resources loaded by user.clj thus impacts startup time for your application.

Adding libraries for interactive use

There are many development-time cases where it would be useful to add a library interactively without restarting the JVM - speculative evaluation, adding a known dependency to your project, or adding a library to accomplish a specific task.

The Clojure CLI can be used to declare dependencies loaded at REPL startup time. Since Clojure 1.12, you can also dynamically load libraries at the REPL for interactive use. These functions are available in the clojure.repl.deps namespace:

  • add-lib takes a lib that is not available on the classpath, and makes it available by downloading (if necessary) and adding to the classloader. Libs already on the classpath are not updated. If the coordinate is not provided, the newest Maven version or git tag (if the library has an inferred git repo name) are used.

  • add-libs is like add-lib, but resolves a set of new libraries and versions together.

  • sync-deps calls add-libs with any libs present in deps.edn, but not yet present on the classpath.

These new functions are intended only for interactive use at the repl - using a deps.edn is still the proper way to build and maintain your code. To this end, these functions all check that *repl* is bound to true. In a clojure.main REPL, these new functions are automatically referred in the user namespace. In other repls, you may need to (require '[clojure.repl.deps :refer :all]) before use.

tap

tap is a shared, globally accessible system for distributing a series of informational or diagnostic values to a set of (presumably effectful) handler functions. It can be used as a better debug prn, or for facilities like logging etc.

tap> sends a value to the set of taps. Taps can be added with add-tap and will be called with any value sent to tap>. The tap function may (briefly) block (e.g. for streams) and will never impede calls to tap>, but blocking indefinitely may cause tap values to be dropped. If no taps are registered, tap> discards. Remove taps with remove-tap.

Launching a Socket Server

The Clojure runtime now has the ability to start a socket server at initialization based on system properties. One expected use for this is serving a socket-based REPL, but it also has many other potential uses for dynamically adding server capability to existing programs without code changes.

A socket server will be started for each JVM system property like "clojure.server.<server-name>". The value for this property is an edn map representing the configuration of the socket server with the following properties:

  • server-daemon - defaults to true, socket server thread doesn’t block exit

  • address - host or address, defaults to loopback

  • port - positive integer, required

  • accept - namespaced symbol of function to invoke on socket accept, required

  • args - sequential collection of args to pass to accept

  • bind-err - defaults to true, binds *err* to socket out stream

  • client-daemon - defaults to true, socket client thread doesn’t block exit

Additionally, there is a repl function provided that is slightly customized for use with the socket server in clojure.core.server/repl.

Following is an example of starting a socket server with a repl listener. This can be added to any existing Clojure program to allow it to accept external REPL clients via a local connection to port 5555.

-Dclojure.server.repl="{:port 5555 :accept clojure.core.server/repl}"

With the Clojure CLI, use -J flag to pass the option to the JVM (note that this will also start a local REPL in addition to the socket REPL):

clj -J-Dclojure.server.repl="{:port 5555 :accept clojure.core.server/repl}"

An example client you can use to connect to this repl remotely is telnet (could also use netcat):

$ telnet 127.0.0.1 5555
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
user=> (println "hello")
hello

You can instruct the server to close the client repl session by using the special command :repl/quit:

user=> :repl/quit
Connection closed by foreign host.

Also see: