Skip to content

Commit 7acc9ef

Browse files
intro: update 15 mins intro to Clojure
Update and reorganise the 15 mins intro to Clojure syntax resolve: #468 resolve: #475
1 parent 09e531e commit 7acc9ef

File tree

2 files changed

+54
-46
lines changed

2 files changed

+54
-46
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
- cli: add run repl as an explicit section
88
- mkdocs: add automatic light/dark mode
99

10+
## Changed
11+
- intro: update 15 mins intro to Clojure
12+
1013
# 2025-05-16
1114
## Added
1215
- dev: set markdown as repository language with `.gitattributes`

docs/introduction/clojure-in-15-minutes.md

Lines changed: 51 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,18 @@
11
# Clojure in 15 minutes
22

3-
A quick tour of the Clojure syntax and common functions, which is so terse you can read through this page in around 15 minutes and have a basic understanding of the language.
3+
A quick tour of the Clojure syntax and common functions. The syntax is quite minimal so this should take around 15 minutest to read through (it may take longer to get comfortable with).
44

55
!!! HINT "Try the code out in the REPL"
66
[:fontawesome-solid-book-open: Start a Clojure REPL](/clojure/clojure-cli/repl/) or use a [:fontawesome-solid-book-open: Clojure aware editor](/clojure/clojure-editors/) connected to a REPL and experiment with these code examples.
77

88
Using the REPL provides instant feedback on each expression as they are evaluated, greatly increasing your understanding.
99

1010

11-
## Comments
12-
13-
`;;` two semi-colons for a line comment, `;` single semi-colon to comment the rest of the line
14-
15-
`#_` comment reader macro to comment out the next form
16-
17-
`(comment ,,,)` form to comment all the containing forms, useful to [:fontawesome-solid-book-open: separate experimental and established code](/clojure/introduction/repl-workflow/#rich-comment-blocks-living-documentation) in a namespace.
18-
19-
2011
## Clojure expressions
2112

22-
Clojure is mostly written with "expressions", a lists of elements inside parentheses, `()`, separated by space characters.
13+
Clojure is written with "expressions", a lists of elements inside parentheses, `()`, separated by space characters.
14+
15+
An expression is made of one or more forms, a form is a general term for anything that legally evaluates in Clojure, e.g. a number, string function definition, etc.
2316

2417
Clojure evaluates the first element in an expression as a function call. Additional elements in the expression are passed as value arguments to the called function.
2518

@@ -33,6 +26,16 @@ Clojure evaluates the first element in an expression as a function call. Additi
3326
(map inc (range 0 99))
3427
```
3528

29+
30+
## Comment
31+
32+
`;;` two semi-colons for a line comment, `;` single semi-colon to comment the rest of the line
33+
34+
`#_` comment reader macro to skip specific parts of code, e.g. `(+ 1 2 #_(* 3 4))` is read as `(+ 1 2)`
35+
36+
`(comment ,,,)` form to comment all the containing forms, useful to [:fontawesome-solid-book-open: separate experimental and established code](/clojure/introduction/repl-workflow/#rich-comment-blocks-living-documentation) in a namespace. Note that `(comment )` returns `nil` when evaluated so shouldn't be used inside other code.
37+
38+
3639
## Organising Clojure
3740

3841
Clojure code is organised into one or more namespaces. The namespace represents the directory path and file name that contains the code of the particular namespace.
@@ -152,16 +155,16 @@ A predicate is a function that returns a boolean `true` or `false` value and by
152155

153156
The most common data collections in Clojure:
154157

155-
* `(1 2 "three")` or `(list 1 2 "three")` - a list of values read from start to end (sequential access)
156-
* `[1 2 "three"]` or `(list 1 2 "three")` - a vector of values with index (random access)
157-
* `{:key "value"}` or `(hash-map :key "value")` - a hash-map with zero or more key value pairs (associative relation)
158-
* `#{1 2 "three"}` or `(set 1 2 "three")` - a unique set of values
158+
* List literal `(1 2 "three")` or function call `(list 1 2 "three")` - a list of values read from start to end (sequential access)
159+
* Vector literal `[1 2 "three"]` or function call `(vector 1 2 "three")` - a vector of values with index (random access)
160+
* Hash-map literal `{:key "value"}` or function call `(hash-map :key "value")` - a hash-map with zero or more key value pairs (associative relation)
161+
* Set literal `#{1 2 "three"}` or function call `(set 1 2 "three")` - a unique set of values
159162

160-
A list `()` is evaluated as a function call. The first element of the list the name of the function to call and additional values are arguments to the function.
163+
A literal list `()` expression is evaluated as a function call. The first element in the list is the name of the function to call and additional values are arguments to the function (assuming the function takes arguments).
161164

162-
The `'` quote function informs the Clojure reader to treat the list as data only.
165+
The `'` is syntax short-cut for the `quote` function which informs the Clojure reader to treat a list as data only. A quoted list is not evaluated as a function.
163166

164-
!!! EXAMPLE "A quoted list is treated as data"
167+
!!! EXAMPLE "Evaluating a quoted list returns that list as data"
165168
```clojure
166169
'(1 2 3) ; => (1 2 3)
167170
```
@@ -180,11 +183,12 @@ Only lists are sequences
180183

181184
Sequences are an interface for logical lists, which can be lazy. "Lazy" means that a sequence of values are not evaluated until accessed.
182185

183-
A lazy sequence enables the use of large or even an infinite series, like so:
186+
The `range` function generates an 'infinite' series of numbers, e.g. `(range) ; => (0 1 2 3 4 ...)`
187+
188+
Lazily evaluating a sequence enables the use of large or even an infinite series without consuming all the computer memory, like so:
184189

185190
!!! EXAMPLE "Lazy sequences"
186191
```clojure
187-
(range) ; => (0 1 2 3 4 ...) - an infinite series
188192
(take 4 (range)) ; (0 1 2 3) - lazyily evaluate range and stop when enough values are taken
189193
```
190194

@@ -202,20 +206,20 @@ Use conj to add an item relative to the type of collection, to the beginning of
202206
(conj '(1 2 3) 4) ; => (4 1 2 3)
203207
```
204208

205-
Use concat to add lists or vectors together
209+
Use `concat` (concatenate) to add sequences (lists or vectors) together
206210

207211
```clojure
208212
(concat [1 2] '(3 4)) ; => (1 2 3 4)
209213
```
210214

211-
Use filter, map to interact with collections
215+
`filter` maps another function over a collection of values, returning the values that returned true from the function used by filter
212216

213217
```clojure
214218
(map inc [1 2 3]) ; => (2 3 4)
215219
(filter even? [1 2 3]) ; => (2)
216220
```
217221

218-
Use reduce to reduce them
222+
`reduce` uses a function over a collection of values to return a combined result
219223

220224
```clojure
221225
(reduce + [1 2 3 4])
@@ -230,18 +234,18 @@ Reduce can take an initial-value argument too
230234
; => [3 2 1]
231235
```
232236

233-
Equivalent of `(conj (conj (conj [] 3) 2) 1)`
237+
The above is the equivalent of `(conj (conj (conj [] 3) 2) 1)`
234238

235239

236-
## Annonymous Functions
240+
## Anonymous Functions
237241

238-
Use `fn` to create new functions that defines some behaviour. `fn` is referred to as an anonymous fuction as it has no external name to be referenced by and must be called within a list form.
242+
Use `fn` to create new functions that defines some behaviour. `fn` is referred to as an anonymous function as it has no external name to be referenced by and must be called within a list form.
239243

240244
```clojure
241245
(fn hello [] "Hello World")
242246
```
243247

244-
Wrap a `(fn ,,,)` form in parens to call it and return the result.
248+
Wrap an anonymous function `(fn ,,,)` expression in another list to call it and return the result.
245249

246250
!!! EXAMPLE "Call an anonymous function"
247251
```clojure
@@ -267,7 +271,7 @@ The `var` name bound to the function can now be called anywhere in the namespace
267271
(fn hello [] "Hello World"))
268272
```
269273

270-
!!! EXAMPLE "Evaluate annonymous function by evaluating its name"
274+
!!! EXAMPLE "Evaluate anonymous function by evaluating its name"
271275
```clojure
272276
hello-world
273277
```
@@ -287,7 +291,7 @@ It is more common to use the `defn` macro to define a function. This is the sam
287291
"Hello World")
288292
```
289293

290-
`#'user/hello-world` is the value returned from evaluating the expression, showing the fully qualified name of the function. Note: the fully qualified name will be different when defined in a differnt namespace than `user`.
294+
`#'user/hello-world` is the value returned from evaluating the expression, showing the fully qualified name of the function. Note: the fully qualified name will be different when defined in a different namespace than `user`.
291295

292296

293297
> A `defn` function has the scope of the current namespace, so can be called anywhere in the namespace or in a namepace that has used `require` to include this namespace.
@@ -328,7 +332,7 @@ The correct number of arguments must be used when calling a function, or an erro
328332
```
329333

330334

331-
Clojure supports multi-variadic functions, allowing one function definition to respond to a function call with different number of arguments. This provides a simple form of polymorphism based on the number of arguments.
335+
Clojure supports multi-variadic functions, allowing one function definition to respond to a function call with different number of arguments. This provides a simple form of polymorphism based on the number of arguments.
332336

333337
```clojure
334338
(defn hello-polly
@@ -416,13 +420,13 @@ Use assoc to add new keys to hash-maps
416420
(assoc keymap :d 4) ; => {:a 1, :b 2, :c 3, :d 4}
417421
```
418422

419-
But remember, clojure types are immutable!
423+
But remember, Clojure types are immutable!
420424

421425
```clojure
422426
keymap ; => {:a 1, :b 2, :c 3}
423427
```
424428

425-
Use dissoc to remove keys
429+
Use `dissoc` function to remove keys from a hash-map
426430

427431
```clojure
428432
(dissoc keymap :a :b) ; => {:c 3}
@@ -435,13 +439,13 @@ Use dissoc to remove keys
435439
(set [1 2 3 1 2 3 3 2 1 3 2 1]) ; => #{1 2 3}
436440
```
437441

438-
Add a member with conj
442+
Add a value to a set with `conj` (conjoin)
439443

440444
```clojure
441445
(conj #{1 2 3} 4) ; => #{1 2 3 4}
442446
```
443447

444-
Remove one with disj
448+
Remove one value from a set with `disj` (disjoin)
445449

446450
```clojure
447451
(disj #{1 2 3} 1) ; => #{2 3}
@@ -454,7 +458,7 @@ Test for existence by using the set as a function:
454458
(#{1 2 3} 4) ; => nil
455459
```
456460

457-
There are more functions in the clojure.sets namespace.
461+
There are more functions in the [clojure.sets namespace](https://clojure.github.io/clojure/clojure.set-api.html).
458462

459463
## Useful forms
460464

@@ -480,7 +484,7 @@ Group statements together with do
480484
"World") ; => "World" (prints "Hello")
481485
```
482486

483-
Functions have an implicit do
487+
Functions have an implicit `do` function that will call every expression within a function definition.
484488

485489
```clojure
486490
(defn print-and-say-hello [name]
@@ -489,7 +493,7 @@ Functions have an implicit do
489493
(print-and-say-hello "Jeff") ;=> "Hello Jeff" (prints "Saying hello to Jeff")
490494
```
491495

492-
So does let
496+
The `let` function also has an implicit `do` function.
493497

494498
```clojure
495499
(let [name "Urkel"]
@@ -499,13 +503,13 @@ So does let
499503

500504
## Namespaces and Libraries
501505

502-
Namespaces are used to organise code into logical groups. The top of each Clojure file has an `ns` form that defines the namespace name. The domain part of the namespace name is typically the organisation or community name (e.g. GitHub user/organisation)
506+
Namespaces are used to organise code into logical groups. The top of each Clojure file has an `ns` form that defines the namespace name. The domain part of the namespace name is typically the organisation or community name (e.g. GitHub organisation or user account name)
503507

504508
```clojure
505509
(ns domain.namespace-name)
506510
```
507511

508-
All Practicalli projects have namespace domains of `practicalli`
512+
Practicalli projects use a namespace domain of `practicalli` followed by the project name
509513

510514
```clojure
511515
(ns practicalli.service-name)
@@ -524,7 +528,7 @@ A required directive is typically added to a namespace form
524528
(require [clojure.set :as set]))
525529
```
526530

527-
The functions from clojure.set can be used via the alias name, rather than the fully qualified name, i.e. `clojure.set/intersection`
531+
Functions from `clojure.set` can be used via the alias name, `set/intersection`, rather than the fully qualified name, `clojure.set/intersection`
528532

529533
```clojure
530534
(set/intersection #{1 2 3} #{2 3 4}) ; => #{2 3}
@@ -540,7 +544,7 @@ The functions from clojure.set can be used via the alias name, rather than the f
540544
[clojure.set :as set]))
541545
```
542546

543-
`require` can be used by itself, usually within a rich code block
547+
`require` can be used as a top level expression (not in an `ns` expression). Requires are often used within a rich code block for experimenting with code.
544548

545549
```clojure
546550
(comment
@@ -552,7 +556,7 @@ The functions from clojure.set can be used via the alias name, rather than the f
552556

553557
Clojure is strongly typed, so everything is a type in Clojure.
554558

555-
Clojure is dynamically typed, so Clojure infers the type. A type does not need to be specified in the code, making the code simpler and more concise.
559+
Clojure is dynamically typed, so Clojure infers the type. An explicit type name does not need to be specified in the code, making the code simpler and more concise.
556560

557561
Clojure is a hosted language and uses the type system of the platform it runs upon. For example, Clojure uses Java object types for booleans, strings and numbers under the covers.
558562

@@ -566,7 +570,7 @@ Use `class` or `type` function to inspect the type of some code in Clojure.
566570
(type nil); The "null" value is called nil
567571
```
568572

569-
Vectors and Lists are java classes too!
573+
Collections in Clojure have their own type too.
570574

571575
```
572576
(type [1 2 3]); => clojure.lang.PersistentVector
@@ -577,17 +581,18 @@ Vectors and Lists are java classes too!
577581
Type hints can be used to avoid reflection look-ups where performace critical issues have been identified. Type hints are not required in general.
578582
[Clojure Type Hints](https://clojure.org/reference/java_interop#typehints){target=_blank .md-button}
579583

584+
580585
## Java Interop
581586

582-
Java has a huge and useful standard library, so you'll want to learn how to get at it.
587+
Java has a large and very useful standard library which is easily accessible from Clojure. The `java.lang` library is available by default.
583588

584589
Use import to load a java package
585590

586591
```clojure
587-
(import java.util.Date)
592+
(import java.util.*)
588593
```
589594

590-
Or import from a java package name
595+
Import specific Java classes
591596

592597
```clojure
593598
(ns test

0 commit comments

Comments
 (0)