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
", " ["element 1" "element 2" "element 3"]) (clojure.string/join
element 1, element 2, element 3
Includes?
"Damien" "en") (clojure.string/includes?
true
Characters
Clojure characters are Java characters
\a
Special 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:
: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
:
nil) (some?
false
1) (some?
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))
("Damien") (greet
Hello, Damien
Which is syntactic sugar for:
def greet (fn [name] (str "Hello, " name)))
("Damien") (greet
Hello, Damien
Multi-Arity Functions
defn greet
("World!"))
([] (greet 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 #(= 4 %) '(1 2 3))) (some? (
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
equality
of the data. - "Two objects that differ only in metadata are equal."
meta #'+)) (pprint (
{: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]) (
"a b c") #" ") (str/split (str/upper-case
["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:
"blob.txt" "toast") (spit
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