Damien Gonot
Home Blog Notes About

Clojure

Homepage / Notes / Computer Science / Programming Languages / Clojure

A Lisp on the JVM

Language Features

Misc

Numbers

(+ 1 1)
2

Range

(range 5)
(0 1 2 3 4)
(range 5 10)
(5 6 7 8 9)
(range 10 20 2)
(10 12 14 16 18)

Strings

"Damien"
Damien
(apply str (reverse "Damien"))
neimaD

String Interpolation

(def name "Damien")
(str "Hello " name)
Hello Damien

Join elements in lists

(clojure.string/join ", " ["element 1" "element 2" "element 3"])
element 1, element 2, element 3

Includes?

(clojure.string/includes? "Damien" "en")
true

Characters

Clojure characters are Java characters

\a

Special characters

special characterresult
\newline"\n"
\space" "
\tab"\t"
\formfeed"\f"
\backspace"\b"
\return"\r"

Keywords

Keywords are symbolic identifiers that evaluate to themselves.

Example:

:foo

Convert to keyword

(keyword 'foo)
:foo
(keyword "foo")
:foo

Check if keyword

(keyword? :foo)
true
(keyword? "foo")
false

Variables

(def x 42)
x
42

defonce

defonce defines variable only if it has never been defined before:

(defonce y 9)
y
9

If you try to re-define it, it won't work:

(defonce y 10)
y
9

Array destructuring

(let [[a b c] '(1 2 3)] (println a b c))
1 2 3

nil

Check if something is nil:

(nil? nil)
true

Check if something is NOT nil:

(some? nil)
false
(some? 1)

Scope

let can be used to define a variable in a specific scope:

(let [z 5] z)
5

z is not defined outside of the scope:

z
class clojure.lang.Compiler$CompilerException

Functions

"defn" = "def" + "fn" (define function)

(defn greet [name] (str "Hello, " name))
(greet "Damien")
Hello, Damien

Which is syntactic sugar for:

(def greet (fn [name] (str "Hello, " name)))
(greet "Damien")
Hello, Damien

Multi-Arity Functions

(defn greet
  ([] (greet "World!"))
  ([name] (str "Hello, " name)))

(println (greet "Damien"))
(println (greet))
Hello, Damien
Hello, World!

Anonymous Function Syntax

#() with % used as arguments (%1, %2 for first and second arguments)

(map #(* 2 %) '(1 2 3))
(2 4 6)

Which is syntactic sugar for:

(map (fn [x] (* 2 x)) '(1 2 3))
(2 4 6)

Data Structures

Collections

All Clojure collections are immutable and persistent.

Lists
'(1 2 3)
(1 2 3)

Which is syntactic sugar for:

(list 1 2 3)
(1 2 3)
First
(first '(1 2 3))
1
Last
(last '(1 2 3))
3
Count
(count '(1 2 3))
3
Take
(take 2 '(1 2 3 4))
(1 2)

Take Last

(take-last 2 '(1 2 3 4))
(3 4)
Check if element in list
(some #(= 3 %) '(1 2 3))
true

(some) alone only returns nil, not false

(some? (some #(= 4 %) '(1 2 3)))
false
Frequencies
(frequencies '("a" "b" "b" "c" "c" "c"))
{"a" 1, "b" 2, "c" 3}
Sort
(sort '(2 3 1))
(1 2 3)
Remove
(remove #{"the" "a" "ate"} '("the" "monkey" "ate" "a" "banana"))
("monkey" "banana")
Vectors
[1 2 3]
[1 2 3]

Which is syntactic sugar for:

(vector 1 2 3)
[1 2 3]
Sets

Collections of unique values.

#{1 2 3}
#{1 3 2}

Which is syntactic sugar for:

(set [1 2 3])
#{1 3 2}

Can be used to remove duplicates from a vector:

(set [1 2 3 1 2 3])
#{1 3 2}

Maps

{:a 1, :b 2}
{:a 1, :b 2}

Which is syntactic sugar for:

(hash-map :a 1, :b 2)
{:b 2, :a 1}

Comma is actually not required:

{:a 1 :b 2}
{:a 1, :b 2}
Get
(get {:a 1 :b 2} :a)
1

"get" can be omitted

({:a 1 :b 2} :b)
2
Get-in
(get-in {:error {:code 404 :message "Wrong location"}} [:error :code])
404

Conditionals

If/if-not statements

(if true "this is true" "this is false")
this is true
(if false "this is true" "this is false")
this is false

When/when-not

Only handles "true", and wrapped in an implicit (do) statement

(when true (println "this is true") (println "this is also true"))
this is true
this is also true

Cond

(def x 10)
(cond (= x 10) "true")
true

Note: for this type of conds, if the first is true, it won't continue

(def x (rand-int 50))
(cond
  (> x 40) "more than 40"
  (> x 30) "more than 30"
  (> x 20) "more than 20"
  (> x 10) "more than 10"
  :else "neither")
more than 30

Another example

(def x (rand-int 50))
(cond
  (> x 40) "more than 40"
  (> x 30) "more than 30"
  (> x 20) "more than 20"
  (> x 10) "more than 10"
  :else "neither")
neither

Case

(let [name "Damien"]
  (case name
    "Damien" "my real name"
    "Jane" "my neighbour's name"
    "John" "my colleague's name"))
my real name
(let [name "Jane"]
  (case name
    "Damien" "my real name"
    "Jane" "my neighbour's name"
    "John" "my colleague's name"))
my neighbour's name

Can provide a default:

(let [name "Hugo"]
  (case name
    "Damien" "my real name"
    "Jane" "my neighbour's name"
    "John" "my colleague's name"
    "Unknown name"))
Unknown name

Can "match" multiple values using a list:

(let [name "Jane"]
  (case name
    ("Damien" "Jane" "John") "an acquaintance"
    "not an acquaitance"))
an acquaintance

Higher-order functions

Map

(map #(* 3 %) '(1 2 3))
(3 6 9)

Reduce

(reduce + '(3 3 3))
9

Filter

(filter odd? (range 10))
(1 3 5 7 9)

Metadata

(pprint (meta #'+))
{:added "1.2",
 :ns #namespace[clojure.core],
 :name +,
 :file "clojure/core.clj",
 :inline-arities #function[clojure.core/>1?],
 :column 1,
 :line 986,
 :arglists ([] [x] [x y] [x y & more]),
 :doc
 "Returns the sum of nums. (+) returns 0. Does not auto-promote\n  longs, will throw on overflow. See also: +'",
 :inline #function[clojure.core/nary-inline/fn--5606]}
(def m ^:hi [1 2 3])
(meta (with-meta m {:bye true}))
{:bye true}

Threading Macros

Thread-First

(require '[clojure.string :as str])
(str/split (str/upper-case "a b c") #" ")
["A" "B" "C"]

Becomes

(-> "a b c" (str/upper-case) (str/split #" "))
["A" "B" "C"]

Thread-Last

Insert as last argument

(->> (range 10) (filter odd?))
(1 3 5 7 9)

Spit / Slurp

Write to file:

(spit "blob.txt" "toast")
nil

Read from file:

(slurp "blob.txt")
toast

Can be used over HTTP:

(slurp "https://dummyjson.com/quotes/1")
{"id":1,"quote":"Life isn’t about getting and having, it’s about giving and being.","author":"Kevin Kruse"}

ClojureScript

JavaScript interop

https://lwhorton.github.io/2018/10/20/clojurescript-interop-with-javascript.html

Google Closure Library

https://google.github.io/closure-library/api/

Packages

Reagent

nbb

https://github.com/babashka/nbb

Babahska for ClojureScript / Node.js

scittle

https://github.com/babashka/scittle

The Small Clojure Interpreter exposed for usage in browser script tags

shadow-cljs

https://github.com/thheller/shadow-cljs

ClojureScript compilation made easy

secretary

https://github.com/clj-commons/secretary

A client-side router for ClojureScript

Cherry

https://github.com/squint-cljs/cherry

Experimental ClojureScript to ES6 module compiler

Squint

https://github.com/squint-cljs/squint

ClojureScript syntax to JavaScript compiler

Resources

https://cljs.info/cheatsheet/

ClojureScript for React Developer

https://www.youtube.com/playlist?list=PLUGfdBfjve9VGzp7G1C9FYfH8Yk1Px-11

Packages

babashka

https://github.com/babashka/babashka

Native, fast starting Clojure interpreter for scripting

clj-kondo

https://github.com/clj-kondo/clj-kondo

A linter for Clojure code that sparks joy.

Coast

https://coast.swlkr.com/

The Fullest Full Stack Clojure Web Framework

Hiccup

https://github.com/weavejester/hiccup

Fast library for rendering HTML in Clojure

ClojureDart

https://github.com/Tensegritics/ClojureDart

A port of Clojure that compiles to Dart (mostly for Flutter framework)

DataScript

https://github.com/tonsky/datascript

Immutable database and Datalog query engine for Clojure, ClojureScript and JS

Quickdoc

https://github.com/borkdude/quickdoc

Quick and minimal API doc generation for Clojure

Pedestal

http://pedestal.io/

Pedestal is a set of libraries that we use to build services and applications. It runs in the back end and can serve up whole HTML pages or handle API requests.

Portal

https://github.com/djblue/portal

A clojure tool to navigate through your data.

Leiningen

https://leiningen.org/

Automate Clojure projects without setting your hair on fire.

Neil

https://github.com/babashka/neil

A CLI to add common aliases and features to deps.edn-based projects.

Krell

https://github.com/vouch-opensource/krell

Simple ClojureScript React Native Tooling

Resources

Syntax

Clojure by Example

https://kimh.github.io/clojure-by-example/

Lambda Island videos

List Comprehension with clojure.core/for

https://lambdaisland.com/episodes/list-comprehension-clojure-for

Seq and Seqable

https://lambdaisland.com/episodes/clojure-seq-seqable

Using JavaScript libraries in ClojureScript

https://lambdaisland.com/episodes/javascript-libraries-clojurescript

Clojure Keyword Arguments

https://lambdaisland.com/episodes/clojure-keyword-arguments

ClojureScript Interop

https://lambdaisland.com/episodes/clojurescript-interop

Clojure Docs

https://clojuredocs.org/