├── .gitignore
├── test
└── practicalli
│ └── fifteen_minutes_test.clj
├── README.md
├── deps.edn
└── src
└── practicalli
└── fifteen_minutes.clj
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | *.jar
5 | *.class
6 | /.cpcache
7 | /.lein-*
8 | /.nrepl-history
9 | /.nrepl-port
10 | .hgignore
11 | .hg/
12 |
--------------------------------------------------------------------------------
/test/practicalli/fifteen_minutes_test.clj:
--------------------------------------------------------------------------------
1 | (ns practicalli.fifteen-minutes-test
2 | (:require [clojure.test :refer :all]
3 | [practicalli.fifteen-minutes :refer :all]))
4 |
5 | (deftest a-test
6 | (testing "FIXME, I fail."
7 | (is (= 0 1))))
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # fifteen-minutes
2 |
3 | Examples that walk through the syntax and basic concepts of the Clojure programming language.
4 |
5 | ## Usage
6 |
7 | Open in your favourite Clojure editor and start a REPL.
8 |
9 |
10 | ## License
11 |
12 | Copyright © 2020 Practicalli
13 |
14 | Distributed under the Creative Commons Attribution Share-Alike 4.0 International
15 |
--------------------------------------------------------------------------------
/deps.edn:
--------------------------------------------------------------------------------
1 | {:paths ["src" "resources"]
2 | :deps {org.clojure/clojure {:mvn/version "1.10.1"}}
3 | :aliases
4 | {:test {:extra-paths ["test"]
5 | :extra-deps {org.clojure/test.check {:mvn/version "0.10.0"}}}
6 | :runner
7 | {:extra-deps {com.cognitect/test-runner
8 | {:git/url "https://github.com/cognitect-labs/test-runner"
9 | :sha "f7ef16dc3b8332b0d77bc0274578ad5270fbfedd"}}
10 | :main-opts ["-m" "cognitect.test-runner"
11 | "-d" "test"]}
12 | :jar {:extra-deps {seancorfield/depstar {:mvn/version "0.5.0"}}
13 | :main-opts ["-m" "hf.depstar.jar" "fifteen-minutes.jar"]}}}
14 |
--------------------------------------------------------------------------------
/src/practicalli/fifteen_minutes.clj:
--------------------------------------------------------------------------------
1 | (ns practicalli.fifteen-minutes)
2 |
3 |
4 | ;; Simple Values - Hello World and Maths
5 |
6 | ;; str will create a string out of all its arguments
7 |
8 | (str "Hello Clojure World," " " "happy " 13 " th birthday in 2020")
9 |
10 |
11 | ;; Math uses function calls rather than operators
12 | ;; parentheses () define the order of evaluation
13 | ;; parentheses ensure there is no ambiguity in the order of calculation
14 |
15 | (+ 1 1) ; => 2
16 | (- 24 4 10) ;; => 10
17 | (* 1 2 (+ 1 2) (* 2 2)) ;; => 24
18 |
19 | ;; A ratio is a value in Clojure, helping to maintain precision
20 | (/ 22 7) ;; => 22/7
21 |
22 | ;; A ratio can be coerced into a number type
23 | (/ 22 7.0) ;; => 3.142857142857143
24 |
25 | ;; Equality is comparing values with the = function
26 | ;; assignment is not done with the = function
27 | (= 1 1) ; => true
28 | (= 2 1) ; => false
29 | (odd? 1) ;; => true
30 |
31 | ;; You need not for logic, too
32 | (not true) ; => false
33 | (not= 1 2 3) ;; => true
34 |
35 |
36 | ;; Collections & Sequences
37 | ;;;;;;;;;;;;;;;;;;;
38 |
39 | ;; A list would be written as just (1 2 3), but we have to quote
40 | ;; it to stop the reader thinking it's a function.
41 | ;; Also, (list 1 2 3) is the same as '(1 2 3)
42 |
43 | ;; Both lists and vectors are collections:
44 | (coll? '(1 2 3)) ; => true
45 | (coll? [1 2 3]) ; => true
46 | (coll? {:key "value"}) ;; => true
47 | (coll? #{1 2 3 4}) ;; => true
48 |
49 | ;; Only lists are seqs.
50 | (seq? '(1 2 3)) ; => true
51 | (seq? [1 2 3]) ; => false
52 | (seq? {:key "value"}) ;; => false
53 | (seq? #{1 2 3 4}) ;; => false
54 |
55 |
56 | ;; Seqs are an interface for logical lists, which can be lazy.
57 | ;; "Lazy" means that values are only access in the seq when needed
58 |
59 | ;; range generates a sequence of numbers
60 | (range 4) ; => (0 1 2 3)
61 |
62 | ;; (range) ; => (0 1 2 3 4 ...) (an infinite series)
63 | ;; without any arguments range creates an infinite sequence,
64 | ;; so don't use it alone as it will eat all the computer memory
65 |
66 | ;; take specifies the number of values wanted from a collection or sequence.
67 | ;; range stops once take has all the values it needs
68 | (take 4 (range)) ; (0 1 2 3)
69 |
70 | ;; Use cons to add an item to the beginning of a list or vector
71 | (cons 4 [1 2 3]) ; => (4 1 2 3)
72 | (cons 4 '(1 2 3)) ; => (4 1 2 3)
73 |
74 | ;; Use conj to add an item with respect to the type of collection
75 | ;; - a list is a linked list, so values are added at the start
76 | (conj [1 2 3] 4) ; => [1 2 3 4]
77 |
78 | ;; - a vector is an indexed collection, so random access is fast.
79 | ;; values are added to the end of a vector
80 | (conj '(1 2 3) 4) ; => (4 1 2 3)
81 |
82 | ;; Use concat to add lists or vectors together
83 | (concat [1 2] '(3 4)) ; => (1 2 3 4)
84 |
85 | ;; Use filter, map to interact with collections
86 | (map inc [1 2 3]) ; => (2 3 4)
87 | (filter even? [1 2 3]) ; => (2)
88 |
89 | ;; Use reduce to reduce them
90 | (reduce + [1 2 3 4])
91 | ;; = (+ (+ (+ 1 2) 3) 4)
92 | ;; => 10
93 |
94 | ;; Reduce can take an initial-value argument too
95 | ;; typically used as an accumulator
96 | (reduce conj [] '(3 2 1))
97 | ;; = (conj (conj (conj [] 3) 2) 1)
98 | ;; => [3 2 1]
99 |
100 |
101 | ;; Function definitions
102 | ;;;;;;;;;;;;;;;;;;;;;
103 |
104 | ;; Use fn to create new functions. A function always returns
105 | ;; its last expression.
106 | (fn [] "Hello World")
107 | ;; => #function[practicalli.fifteen-minutes/eval7623/fn--7624]
108 |
109 | ;; The fn function creates an anonymous function definition,
110 | ;; which has no external name to be referred by
111 | ;; A function definition with fn needs to be called within another expression
112 | ((fn [] "Hello World")) ; => "Hello World"
113 |
114 | ;; Anonymous functions are used with functions iterating over collections
115 | (map (fn [value] (* value 9)) [1 2 3 4 5]) ;; => (9 18 27 36 45)
116 |
117 | ;; anonymous function has a shorter syntax too
118 | ;; dropping the fn and argument syntax
119 | ;; using %1 %2 etc to represent arguments
120 | (map #(* %1 9) [1 2 3 4 5]) ;; => (9 18 27 36 45)
121 |
122 |
123 | ;; Give a name to values using the def function
124 | ;; names are accessible throughout the namespace
125 | (def clojure-developer-salary "1 million dollars")
126 | ;; => #'practicalli.fifteen-minutes/clojure-developer-salary
127 |
128 | ;; Use the name in other Clojure code
129 | (str "Clojure developers could earn up to " clojure-developer-salary)
130 | ;; => "Clojure developers could earn up to 1 million dollars"
131 |
132 | ;; Define a name for function so you can call it elsewhere in your code
133 | (def hello-world (fn [] "Hello World"))
134 | (hello-world) ; => "Hello World"
135 |
136 | ;; Using defn is the prefered way to give a function a name
137 | ;; It is a macro that is changed to the above code by Clojure when it is read.
138 | (defn hello-world [] "Hello World")
139 |
140 | ;; The [] is the list of arguments for the function.
141 | ;; There can be zero or more arguments
142 | (defn hello [name]
143 | (str "Hello " name))
144 | (hello "Steve") ; => "Hello Steve"
145 |
146 |
147 | ;; #() is a shorthand for defining a functions,
148 | ;; most useful inline
149 | (def hello-terse #(str "Hello " %1))
150 | (hello-terse "Jenny") ;; => "Hello Jenny"
151 |
152 | ;; You can have multi-variadic functions, useful when you have defaults
153 | (defn hello3
154 | ([] "Hello World")
155 | ([name] (str "Hello " name)))
156 |
157 | (hello3 "Jake") ;; => "Hello Jake"
158 | (hello3) ;; => "Hello World"
159 |
160 | ;; Functions can pack extra arguments up in a seq for you
161 | (defn count-args [& args]
162 | (str "You passed " (count args) " args: " args))
163 | (count-args 1 2 3) ;; => "You passed 3 args: (1 2 3)"
164 |
165 | ;; You can mix regular and packed arguments
166 | (defn hello-count [name & args]
167 | (str "Hello " name ", you passed " (count args) " extra args"))
168 |
169 | (hello-count "Finn" 1 2 3)
170 | ;; => "Hello Finn, you passed 3 extra args"
171 |
172 | ;; More arguments can be captured using the & and a name
173 | ;; In the hello-advanced we capture the mandatory name and address
174 | ;; Anything-else is checked to see if its empty and if so, a standard messages is added
175 | ;; If anything-else has values, they are added to the string instead.
176 |
177 | ;; We are using functions from the clojure.string namespace,
178 | ;; so we make that namespace accessible using the require function
179 | (require 'clojure.string)
180 |
181 | (defn hello-advanced [name address & anything-else]
182 | (str "Hello " name
183 | ", I see you live at " address
184 | (if (nil? anything-else)
185 | ". That is all."
186 | (str "and you also do " (clojure.string/join ", " anything-else)))))
187 | ;; => #'clojure-through-code.fifteen-minutes/hello-advanced
188 |
189 | (hello-advanced "John Stevenson" "7 Paradise street" )
190 | ;; => "Hello John Stevenson, I see you live at 7 Paradise street. That is all."
191 |
192 | (hello-advanced "John Stevenson" "7 Paradise street" "cycling" "swimming")
193 | ;; => "Hello John Stevenson, I see you live at 7 Paradise streetand you also do cycling, swimming"
194 |
195 |
196 |
197 | ;; Clojure Hashmaps
198 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
199 | ;; (also referred to as dictionaries in other languages)
200 |
201 | ;; A Clojure hash-map is a persistent data structure
202 | ;; it shared data with copies where relevant
203 | (class {:a 1 :b 2 :c 3}) ;; => clojure.lang.PersistentArrayMap
204 |
205 | ;; Keywords are pointers to themselves, excellent for lookups
206 | (class :a) ; => clojure.lang.Keyword
207 |
208 | ;; Maps can use any type as a key, but usually keywords are best
209 | (def string-keys-map (hash-map "a" 1, "b" 2, "c" 3))
210 | string-keys-map ;; => {"a" 1, "b" 2, "c" 3}
211 |
212 | (def keyword-keys-map (hash-map :a 1 :b 2 :c 3))
213 | keyword-keys-map ;; => {:a 1, :c 3, :b 2} (order is not guaranteed)
214 |
215 | ;; Getting values from maps using keys
216 | (get keyword-keys-map :c)
217 |
218 | ;; default return value can be set if no key in the collection is found
219 | (get keyword-keys-map :d "Sorry, no data") ;; => "Sorry, no data"
220 |
221 | ;; Maps can be called just like a function, with a key as the argument
222 | ;; This is a short-cut to the get function and useful inside inline functions
223 | (string-keys-map "a") ;; => 1
224 | (keyword-keys-map :a) ;; => 1
225 |
226 | ;; A Keyword key can also be used as a function that gets its associated value from the map
227 | (:b keyword-keys-map) ;; => 2
228 |
229 | ;; Retrieving a non-present value returns nil
230 | (string-keys-map "d") ; => nil
231 |
232 | ;; Use assoc to add new keys to hash-maps
233 | (assoc keyword-keys-map :d 4) ;; => {:a 1, :b 2, :c 3, :d 4}
234 |
235 | ;; But remember, clojure types are immutable!
236 | keyword-keys-map ;; => {:a 1, :b 2, :c 3}
237 |
238 | ;; Use dissoc to remove keys
239 | (dissoc keyword-keys-map :a :b) ;; => {:c 3}
240 |
241 |
242 | ;; Sets
243 | ;;;;;;
244 |
245 | (class #{1 2 3}) ; => clojure.lang.PersistentHashSet
246 |
247 | ;; Create a set with only a unique set of values
248 | (set [1 2 3 1 2 3 3 2 1 3 2 1]) ;; => #{1 2 3}
249 |
250 | ;; Add a member with conj
251 | (conj #{1 2 3} 4) ;; => #{1 2 3 4}
252 |
253 | ;; Remove one with disj
254 | (disj #{1 2 3} 1) ;; => #{2 3}
255 |
256 | ;; Test for existence by using the set as a function:
257 | (#{1 2 3} 1) ;; => 1
258 | (#{1 2 3} 4) ;; => nil
259 |
260 | ;; There are more functions in the clojure.sets namespace.
261 |
262 | ;; Useful forms
263 | ;;;;;;;;;;;;;;;;;
264 |
265 | ;; Logic constructs in clojure are just macros, and look like
266 | ;; everything else
267 | (if false "a" "b") ;; => "b"
268 | (if false "a") ;; => nil
269 |
270 | ;; Use let to create temporary bindings
271 | (let [a 1 b 2]
272 | (> a b)) ;; => false
273 |
274 | ;; Group statements together with do
275 | (do
276 | (print "Hello")
277 | "World") ;; => "World" (prints "Hello")
278 |
279 | ;; Functions have an implicit do
280 | (defn print-and-say-hello [name]
281 | (print "Saying hello to " name)
282 | (str "Hello " name))
283 | (print-and-say-hello "Jeff") ;;=> "Hello Jeff" (prints "Saying hello to Jeff")
284 |
285 | ;; So does let
286 | (let [name "Jenny"]
287 | (print "Saying hello to " name)
288 | (str "Hello " name)) ; => "Hello Jenny" (prints "Saying hello to Jenny")
289 |
290 | ;; Libraries
291 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
292 |
293 | ;; require gives access to all the functions from another namespace
294 | (require 'clojure.set)
295 |
296 | ;; Now we can use set operations with their full name
297 | (clojure.set/intersection #{1 2 3} #{2 3 4}) ;; => #{2 3}
298 | (clojure.set/difference #{1 2 3} #{2 3 4}) ;; => #{1}
299 |
300 | ;; require can specify an alias for a namespace
301 | ;; please use meaningful aliases
302 | (require '[clojure.string :as string] )
303 |
304 | ;; Use / to call functions from a module
305 | (string/blank? "") ; => true
306 |
307 | (string/replace "This is a test." #"[a-o]" string/upper-case) ;; => "THIs Is A tEst."
308 | ;; (#"" denotes a regular expression)
309 |
310 | ;; You can use require in a namespace definition using :require.
311 | ;; You don't need to quote your modules if you do it this way.
312 | ;; and multiple namespaces can be added using one :require
313 | (ns test
314 | (:require
315 | [clojure.string :as str]
316 | [clojure.set :as set]))
317 |
318 |
319 |
320 | ;; Types underlying Clojure
321 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
322 |
323 | ;; Types are inferred by Clojure so mostly not specified
324 | ;; Interop with the host platform (i.e. Java) may benefit from explicit type coercion
325 |
326 | ;; Clojure uses Java's object types for booleans, strings and numbers as these are immutable.
327 | ;; Use `class` or `type` to inspect them.
328 | (class 1) ; Integer literals are java.lang.Long by default
329 | (class 1.); Float literals are java.lang.Double
330 | (class ""); Strings always double-quoted, and are java.lang.String
331 | (class false) ; Booleans are java.lang.Boolean
332 | (class nil); The "null" value is called nil
333 |
334 | ;; If you want to create a literal list of data, use ' to make a "symbol"
335 | '(+ 1 2) ; => (+ 1 2)
336 |
337 | ;; You can eval symbols.
338 | (eval '(+ 1 2)) ; => 3
339 |
340 | ;; Vectors and Lists are java classes too!
341 | (class [1 2 3]); => clojure.lang.PersistentVector
342 | (class '(1 2 3)); => clojure.lang.PersistentList
343 |
344 |
345 |
346 | ;; Java
347 | ;;;;;;;;;;;;;;;;;
348 |
349 | ;; Java has a huge and useful standard library, so
350 | ;; you'll want to learn how to get at it.
351 |
352 | ;; Use import to load a java module
353 | (import java.util.Date)
354 |
355 | ;; You can import from an ns too.
356 | (ns test
357 | (:import java.util.Date
358 | java.util.Calendar))
359 |
360 | ;; Use the class name with a "." at the end to make a new instance
361 | (Date.) ;;
362 |
363 | ;; Use . to call methods. Or, use the ".method" shortcut
364 | (. (Date.) getTime) ;;
365 | (.getTime (Date.)) ;; exactly the same thing.
366 |
367 | ;; Use / to call static methods
368 | (System/currentTimeMillis) ;; (system is always present)
369 |
370 | ;; Use doto to make dealing with (mutable) classes more tolerable
371 | (import java.util.Calendar)
372 | (doto (Calendar/getInstance)
373 | (.set 2000 1 1 0 0 0)
374 | .getTime) ;; => A Date. set to 2000-01-01 00:00:00
375 |
--------------------------------------------------------------------------------