A Clojure program is made of libs: named units of source that each define one namespace. Clojure itself has no project file and no build step; the classpath decides which files it can find, and every file is read form by form (see the reader).
Extensions
| Extension | Holds |
|---|---|
.clj | Clojure for the JVM |
.cljc | code shared by platforms: it is read with reader conditionals allowed. Clojure 1.7 introduced it |
.cljs | ClojureScript |
.cljr | ClojureCLR (.NET) |
.cljd, .joke, .bb | ClojureDart, Joker, Babashka (see dialects) |
.edn | data only, see EDN |
.class | a compiled lib, produced by ahead-of-time compilation |
Namespaces and paths
A lib name is a symbol with usually two or more parts separated by dots. Its file is found on the classpath at a path made from the name: dots become slashes and hyphens become underscores, and the path ends in .class, .clj or .cljc. So com.my-company.utils lives in com/my_company/utils.clj. The lib begins with an ns form that creates the namespace of the same name and declares what it depends on.
(ns com.my-company.clojure.examples.my-utils
(:import java.util.Date)
(:use [clojure.string :only (join)])
(:require [clojure.java.io :as jio]))
com/my_company/clojure/examples/my_utils.clj.The ns form is described on Namespaces and vars.
Load order
When require, use or load looks for a lib, it may find a compiled .class, a .clj and a .cljc. The rules are two:
- a
.classfile is preferred over source, unless the source file is newer than the class file; - a
.cljfile is preferred over a.cljcfile. A library author can ship a portable.cljcand add a platform-specific.cljthat overrides it.
Files with a fixed name
| Name | Role |
|---|---|
user.clj | loaded by the runtime at start-up if it is on the classpath. It is meant for development helpers and is not recommended for production, because everything it loads slows start-up |
data_readers.clj, data_readers.cljc | at the root of the classpath: a map from a tagged-literal tag symbol to the fully qualified var that reads it. The .cljc form is read with reader conditionals |
deps.edn | the dependencies and aliases of the clj and clojure tools, see Tooling |
project.clj | the project file of Leiningen: one defproject form |
build.boot | the build file of Boot |
bb.edn | Babashka's configuration and task file |
shadow-cljs.edn | the build configuration of shadow-cljs |
Running a file
- A namespace with a
-mainfunction can be started by name:clojure -M -m my.app. The runtime calls-mainwith the command-line arguments as strings. - A script is loaded and run form by form:
clojure -M script.clj(clj -M /path/to/myscript.clj arg1 arg2passes arguments). A first line that starts#!is read as a comment, so a script can begin with a shebang line (Babashka scripts start#!/usr/bin/env bb). (load "/my/app/core")loads a resource by its classpath-relative path without the extension (a path that does not begin with a slash is taken relative to the root directory of the current namespace);(load-file "path.clj")loads a file by file path.- The
:gen-classclause ofnsmakes the namespace compile to a Java class with amainthat calls-main, when the namespace is compiled ahead of time; it is ignored when the code is not being compiled.
Ahead-of-time compilation
Clojure compiles everything it loads to JVM bytecode in memory. (compile 'my.domain.lib) writes the result to the directory in *compile-path*, which must be on the classpath. For a namespace it produces a loader class named like the lib with __init added (my/domain/lib__init.class) and one class per function (my/domain/lib$fnname__1234.class), and a stub class for each gen-class. A loader class runs the same static initialiser as loading the source. Reasons to compile ahead of time are to ship without source, to start faster, to name classes for Java, and to avoid runtime bytecode generation and custom class loaders.
Layout of a source file
- an optional header comment (
;;;lines), then thensform with its documentation string, requires and imports; - definitions (
def,defn,defmacro,defprotocol,defrecordand so on), top to bottom, because a name must be defined (ordeclared) before it is used; - optionally a
-mainfunction at the end; - tests of the lib are normally in a parallel namespace named with
-testadded (my.app.core-testintest/my/app/core_test.clj), written withclojure.test.
Convention, not syntax: ; after code on a line, ;; on a line of its own at the indentation of the code, ;;; and ;;;; for headings in a file. A (comment …) form holds code that is not run and is used for notes and REPL experiments.
Encoding and line endings
Source is text, normally UTF-8. A line comment (; and #!) ends at a line feed or a carriage return, so both Unix and Windows line endings work. A comma is white space, and so are the usual white-space characters of Java (Character.isWhitespace), including the form feed. The viewer reads files as UTF-8 and falls back to Windows-1252 when the bytes are not valid UTF-8.
Not described
Known, not yet covered
- Reference: the Clojure runtime's own start-up sequence (
init.clj,clojure.mainoptions) is only touched on; therepl_and_mainpage of the reference was read foruser.cljand-mainonly.
Unknown
- Reference: the exact set of white-space characters comes from the source of
LispReader.java(Character.isWhitespaceor a comma); the Java class itself was not run.
See also
- Namespaces and vars – the
nsform in full - Tooling – deps.edn, Leiningen and the build tools
- Common Lisp: file structure – the same question for Common Lisp