Clojure

Installation

The clojure site suggests using the following:

brew install clojure/tools/clojure

There is also a slightly different formula in homebrew core:

brew install clojure

This one depends_on "openjdk", so it will also install the latest openjdk homebrew package. The first method is preferred, which requires you to set up and configure a JDK yourself.

Project Creation

Minimal

All you need is a project directory with a deps.edn and a src/foo.clj. However it should be namespaced properly (TODO: work on this).

touch $d/deps.edn $d/src/core.clj
echo "{:deps {org.clojure/clojure {:mvn/version \"RELEASE\"}}}" > $d/deps.edn
echo "(ns core) (defn -main [] (println \"hello world\"))" >> src/core.clj
clj -m core

neil

This looks like the best way! Try this out!

My deps-new template

clojure \
    -Sdeps '{:deps
             {net.clojars.cfclrk/minimal
              {:local/root "/Users/cclark/Projects/codenotes/clojure/minimal"}}}' \
                  -Tnew \
                  create \
                  :template cfclrk/minimal \
                  :name $user/$projectName

Leiningen

lein new app cfclrk/$projectName

deps-new

First, make sure deps-new is installed:

clojure -Ttools install \
        io.github.seancorfield/deps-new \
        '{:git/tag "v0.4.9"}' :as new

Create a new app:

clojure -Tnew app :name cfclrk/$projectName

Create a new lib:

clojure -Tnew lib :name cfclrk/$projectName

clj vs clojure

clj is just a program that wraps clojure with readline support.

find /usr/local/Cellar/clojure -name clj \
    | sed -n '2 p' \
    | xargs cat \
    | grep exec
#+RESULTS:
exec rlwrap -r -q '\"' -b "(){}[],^%#@\";:'" "$bin_dir/clojure" "$@"

readline provides support for editing a command in the terminal (e.g. using arrow keys and C-a C-e C-f C-b for movement, and stuff like that.

So, clj just provides better interactivity in the terminal REPL.

Install a package

Find package versions

Say you want to install integrant:

clj -X:deps \
    find-versions \
    :lib integrant/integrant

Add that dependency, then build the project.

Start clj with package

clj -Sdeps '{:deps {cider/orchard {:mvn/version "0.9.2"}}}'

Tools

Ok, so there is:

  1. tools.build
  2. tools.tools

From the tools.build guide:

In the Clojure CLI, "tools" are programs that provide functionality and do not use your project deps or classpath. Tools executed with -T:an-alias remove all project deps and paths, add "." as a path, and include any other deps or paths as defined in :an-alias.

Where do tools live?

Defined by an alias in deps.edn.

Code Format

cljstyle

Install

brew install --cask cljstyle

cljfmt

Format a project using cljfmt. Note, the check argument can also be "fix" to make the changes in-place.

clojure -Sdeps '{:deps {cljfmt/cljfmt {:mvn/version "0.8.0"}}}' \
        -M -m cljfmt.main check

Classpath

This is how cider grabs it:

(seq (.split (System/getProperty "java.class.path") ":"))

Directories on the classpath:

(let [classpath-entries (.split (System/getProperty "java.class.path") ":")
      classpath-as-files (map clojure.java.io/file classpath-entries)
      classpath-dirs (filter #(.isDirectory %) classpath-as-files)]
  classpath-dirs)
#+RESULTS:
(#object[java.io.File 0x43602f98 "src"]
 #object[java.io.File 0x2b9120c6 "resources"])

Cmd Execution and REPL

Call a single function

clj -X $nsname/$funcname
#+RESULTS:
Hello world, the time is 10:00 AM

Start a REPL that I can attach to from Emacs.

clj -Sdeps '{:deps {cider/cider-nrepl {:mvn/version "RELEASE"}}}' \
    -m nrepl.cmdline \
    --middleware "[cider.nrepl/cider-middleware]"

Emacs Cider

Basic workflow is described in the cider docs here.

  • Open a file in the project
  • M-x cider-jack-in
  • Load a project file using C-c C-k (cider-load-buffer)
  • In the *cider-repl buffer, you know call the fully-qualified functions in that file.
    • Or, (in-ns 'the.ns.name) to call functions in that file without namespace qualification.
    • Then also (use 'clojure.core) and (use 'clojure.repl)'
  • Edit a function, and C-c C-e on that function to reload it.

Tips

  • From source file, C-u C-c C-z to jump to cider buffer already namespaced to the source file.
  • Quit a long-running eval with C-c C-b (cider-interrupt).

Doc

Function doc: C-c C-d C-d (cider-doc)

Quickies

Print environment variables

(doseq [[k v] (System/getenv)]
  (println k v))

Clojure and Java versions

*clojure-version*
(System/getProperty "java.version")

Read and write a binary file

(import java.net.URI)
(import java.nio.file.Paths)
(import java.nio.file.Files)

(defn read-file-bytes
  "Load the file at `path` into a byte[].
  `path` is a String relative to the this service's root directory."
  [^String path]
  (let [file-uri (.toURI (io/resource path))
        nio-path (Paths/get file-uri)]
    (Files/readAllBytes nio-path)))

;; Read file
(def mypdf
  (read-file-bytes "resources/my.pdf"))

;; Write file
(let [p (Paths/get
         (URI. "file:///Users/cclark/Downloads/bar.pdf"))
      opts (into-array java.nio.file.OpenOption [])]
  (Files/write p mypdf opts))

Org mode

(use 'inflections.core)
(plural "word")
#+RESULTS:
words
(range 10)
#+RESULTS:
(0 1 2 3 4 5 6 7 8 9)

core.async

There are some great examples from O'Reilly here, which is supplemental content for their excellent video course Communicating Sequential Processes with core.async.