├── .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 | --------------------------------------------------------------------------------