Clojure
Homepage / Notes / Computer Science / Programming Languages / Clojure
A Lisp on the JVM
Language Features
Misc
(print)to print for humans(println)to print with newline for humans(pr)to print for data(prn)to print with newline for data
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
\aSpecial characters
| special character | result |
|---|---|
\newline | "\n" |
\space | " " |
\tab | "\t" |
\formfeed | "\f" |
\backspace | "\b" |
\return | "\r" |
Keywords
Keywords are symbolic identifiers that evaluate to themselves.
Example:
:fooConvert to keyword
(keyword 'foo):foo
(keyword "foo"):foo
Check if keyword
(keyword? :foo)true
(keyword? "foo")false
Variables
(def x 42)
x42
defonce
defonce defines variable only if it has never been defined before:
(defonce y 9)
y9
If you try to re-define it, it won't work:
(defonce y 10)
y9
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:
zclass 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
- https://clojure.org/reference/metadata
- Metadata allows to attach arbitrary annotation to data.
- Metadata isn't considered an integral of the value of the object. It doesn't impact
equalityof the data. - "Two objects that differ only in metadata are equal."
(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
- https://github.com/reagent-project/reagent
- http://reagent-project.github.io/
- A minimalistic ClojureScript interface to React.js
- https://github.com/reagent-project/reagent/blob/master/doc/CreatingReagentComponents.md
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
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
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
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
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
HumbleUI
https://github.com/HumbleUI/HumbleUI
Clojure Desktop UI framework
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