Clojure

Unbind all vars in a namespace

(doseq [[sym var] (ns-publics *ns*)]
  (ns-unmap *ns* sym))

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.

Versions

Clojure version

*clojure-version*

Java version

(System/getProperty "java.version")

Project Creation

Minimal

All you need is a project directory with a deps.edn and a src/core.clj.

(require '[clojure.java.io])

(def deps
  "{:deps
 {org.clojure/clojure {:mvn/version \"RELEASE\"}}}
")

(def core
  "(ns core)

(defn -main
  [& args]
  (println \"hello world!\"))
")

(let [deps-path "hello_world/deps.edn"
      core-path "hello_world/src/core.clj"]
  (clojure.java.io/make-parents deps-path)
  (spit deps-path deps)
  (spit core-path core))

Run with:

clojure -M -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

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

Leiningen

lein new app 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

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

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 d (cider-doc)

Quickies

Print environment variables

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

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")
(range 3)
#js {:foo "bar"}
(= *file* (System/getProperty "babashka.file"))

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.