Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 51 additions & 50 deletions src/clojure_by_example/ex05_immutability_and_fp.clj
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
(ns clojure-by-example.ex05-immutability-and-fp)


;; WORK IN PROGRESS


;; Ex05: Lesson Goals
;; - This section is more conceptual, than exercise-oriented.
;; - Set you up with some important ideas, which we will use heavily
Expand Down Expand Up @@ -59,26 +56,25 @@ pi ; evaluate to confirm

;; - So suppose we define...

(def habitable-planets [{:pname "Earth" :moons 1}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling them habitable-planets to begin with adds a lot of confusion at line 81, where you evaluate habitable-planets and it returns the planets unchanged, which just looks like all planets are habitable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

{:pname "Mars" :moons 2}])
(def planets [{:pname "Earth" :moons 1}
{:pname "Mars" :moons 2}])

;; - Then, maybe, we can `assoc` a new k-v pair into all
;; habitable-planets:
;; - Then, maybe, we can `assoc` a new k-v pair into all planets:
;; - And while we're at it, also dissoc an existing one:
;;
(map (fn [planet]
(assoc (dissoc planet :moons)
:habitable? true))
habitable-planets)
:habitable? true))
planets)


;; EXERCISE:
;;
;; Predict the result of filtering by the value of `:habitable?`:

(filter :habitable? habitable-planets)
(filter :habitable? planets)

habitable-planets ; confirm by checking the value of this
planets ; confirm by checking the value of this



Expand Down Expand Up @@ -118,7 +114,7 @@ habitable-planets ; confirm by checking the value of this
;; At each re-definition, `am-i-mutable` effectively "becomes"
;; the new _immutable_ value.
;;
;; "Immutable" value is actually an oxymoron. Recall, we said that
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Immutable" value is a tautology. "Mutable" value is an oxymoron. We can choose to use one or the other.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ayyo! Yes, /s/Immutable/Mutable

;; "Mutable" value is actually an oxymoron. Recall, we said that
;; ALL values are by definition immutable. 3.141 will always be 3.141
;; for ever and ever, till the end of time.
;;
Expand Down Expand Up @@ -297,8 +293,8 @@ habitable-planets ; confirm by checking the value of this

(defn add-upto-three-nums
([] 0) ; identity of addition
([x] (+ x 0 0))
([x y] (+ x y 0))
([x] x)
([x y] (+ x y))
([x y z] (+ x y z)))

(add-upto-three-nums)
Expand All @@ -308,7 +304,7 @@ habitable-planets ; confirm by checking the value of this
#_(add-upto-three-nums 1 2 3 4) ; will fail


;; Variable aritiy
;; Variable arity
;; - When we don't know in advance how many arguments we
;; will have to handle, but we want to handle them all.

Expand All @@ -322,7 +318,7 @@ habitable-planets ; confirm by checking the value of this
(add-any-numbers 1 2 3 4 5)


;; Multiple _and_ Variable airites, combined
;; Multiple _and_ Variable arities, combined
;; - Guess what + actually is inside?
;;
(clojure.repl/source +) ; evaluate, check the REPL/LightTable console
Expand Down Expand Up @@ -421,26 +417,31 @@ habitable-planets ; confirm by checking the value of this

;; We can also alias the whole hash-map:

(let [add-message (fn [{:keys [pname moons]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These function bodies are starting to get bigger, and putting them in let blocks makes them harder to visually parse.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, let's pull them out. I can do it... Should I?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh you did it... I missed that. 💯

:as planet}] ; alias the hash-map as `planet`
(assoc planet
:message (str "Planet " pname " has "
(or moons 0) " moons.")))]
(map add-message [{:pname "Earth" :moons 1}
{:pname "Mars" :moons 2}
{:pname "Moonless"}]))
(defn add-message-1
[{:keys [pname moons]
:as planet}] ; alias the hash-map as `planet`
(assoc planet
:message (str "Planet " pname " has "
(or moons 0) " moons.")))

(map add-message-1 [{:pname "Earth" :moons 1}
{:pname "Mars" :moons 2}
{:pname "Moonless"}])


;; Finally, we can specify default values directly in the destructuring:
(let [add-message (fn [{:keys [pname moons]
:or {moons 0} ; use 0, if :moons is absent
:as planet}]
(assoc planet
:message (str "Planet " pname " has "
moons " moons.")))]
(map add-message [{:pname "Earth" :moons 1}
{:pname "Mars" :moons 2}
{:pname "Moonless"}]))

(defn add-message-2
[{:keys [pname moons]
:or {moons 0} ; use 0, if :moons is absent
:as planet}]
(assoc planet
:message (str "Planet " pname " has "
moons " moons.")))

(map add-message-2 [{:pname "Earth" :moons 1}
{:pname "Mars" :moons 2}
{:pname "Moonless"}])


;; Further, we can exploit combinations of de-structuring
Expand All @@ -455,21 +456,21 @@ habitable-planets ; confirm by checking the value of this
;;
;; - Now, we can exploit vector and map de-structuring, in combination:
;;
(let [add-message-fn
;; (fn [acc-map [k v]] (body... )) for use in reduce
;; [acc-map [k v is further destructured]]
(fn [acc-map [pname {:keys [moons]
:or {moons 0}
:as pdata}]]
(let [msg (str "Planet " pname " has " moons " moons.")]
(assoc acc-map
pname (assoc pdata :message msg))))]
(reduce add-message-fn
{} ; acc-map
{"Earth" {:moons 1}
"Mars" {:moons 2}
"Moonless" {}
"Nomoon" nil}))

(defn add-message-3
[acc-map [pname {:keys [moons]
:or {moons 0}
:as pdata}]]
(let [msg (str "Planet " pname " has " moons " moons.")]
(assoc acc-map
pname (assoc pdata :message msg))))

(reduce add-message-3
{} ; acc-map
{"Earth" {:moons 1}
"Mars" {:moons 2}
"Moonless" {}
"Nomoon" nil})


;; There are _many_ many ways of de-structuring.
Expand All @@ -484,13 +485,13 @@ habitable-planets ; confirm by checking the value of this
;; - `def` is best used only to define names for truly global values.
;;
;; - `defn` is just a wrapper over `def`, designed specifically to
;; define functions
;; define functions.
;;
;; - We exploit lexical scope to bind values as close as possible to the
;; point of use in code. This greatly improves our ability to reason
;; about our code. And it prevents an explosion of global `def`s.
;;
;; - Write pure functions as far as possible, write pure
;; - Write pure functions as far as possible.
;;
;; - Conveniences like multi-arity and variable-arity functions, with
;; argument de-structuring, help us design better functional APIs.
Expand Down