Clojure Koans 16 Refs Notebook
Hi! In this notebook I will attempt to break down refs in Clojure into skills, questions, and, perhaps, more.
There are spoilers below for the Clojure Koans.
Original Clojure Koans repository: https://github.com/functional-koans/clojure-koans/
{:deps {org.clojure/clojure {:mvn/version "1.10.1"} ;; complient is used for autocompletion ;; add your libs here (and restart the runtime to pick up changes) compliment/compliment {:mvn/version "0.3.9"}}}{:hello (clojure-version)}Initial Thoughts
TBD...
(def the-world (ref "hello"))(def bizarro-world (ref {}));; skill 16001: Define the binding of a reference to a symbol (?);; "In the beginning, there was a word"(= "hello" (deref the-world));; question 16001: What does `ref` stand for in Clojure?;; answer 16001a: "Refs" (short for "references") are Clojure's way of dealing with immutability.;; question 16002: What is a reference in Clojure and what does it do specifically?;; question 16003: How does the `ref` function/macro work in Clojure?;; question 16004: Is there any differences between a reference binding and a non-reference binding, and if so, what are they?;; question 16005: What is the word "word" refering to?;; question 16006: What is dereferencing explained in plain words?;; skill 16002: Dereference a binding/reference (variable?) by using the `deref` function/macro (?);; "You can get the word more succinctly, but it's the same"(= "hello" the-world);; skill 16003: Dereference a binding/reference (variable?) by using the `@` prefix (macro?);; "You can be the change you wish to see in the world."(= "better" (do (dosync (ref-set the-world "better")) the-world));; skill 16004: Update/mutate/replace (?) a reference (?);; question 16007: Why is this wrapped in a `dosync` and `do` combo? ;; skill 16005: Use the `do` macro/function to exercute multiple forms/statements (?) and return the result of the final form/statement (?);; "Alter where you need not replace"(= "better!!!" (let [exclamator (fn [x] (str x "!"))] (dosync (alter the-world exclamator) (alter the-world exclamator) (alter the-world exclamator)) the-world));; question 16008: What is the difference between `ref-set` and `alter`?;; question 16009: What is the difference between replacing and altering?;; skill 16006: Change the value bound to / held by (?) a ref (?) by using `alter`;; knowledge: Asychronous work such as `ref-set`, `ref`, and `deref` needs to happen within a `dosync` to keep the work within a single "transaction" (?) becuase ... (?);; "Don't forget to do your work in a transaction!"(= 0 (do (dosync (ref-set the-world 0)) the-world));; question 16010: What is a "transaction" in Clojure?;; answer 16010a: ~~Anytime the value of a reference or object is changed.~~ (?);; Interactions with mutable containers are considered "transactions".;; Transactions are `do` blocks where references to mutable state are contained within.;; question 16011: What is the role of reification in transactions?;; question 16012: Why are transactions (dosync) necessary/helpful?;; answer 16012a: Transactions enable/allow "roll-back";; ;; skill 16007: Create a transaction by using the `dosync` macro/function (?);; question 16013: What does `do` do?;; answer 16013a: The `do` makes the evaluation of conatined `do-sync`s eager.;; question 16014: What does "eager" mean in the context of transations?;; question 16015: What is the difference between "eager" and "lazy"?;; answer 16015a: Conceptually speaking, lazy means to evaluate/execute only as needed, versus eager which means to evaluate/execute things as soon as they are encountered.;; question 16016: What are concrete, concise examples of "eager" vs "lazy";; question 16017: What is the relevant context of "eager" and "lazy"?;; answer 16017a: Data structures can be implemented themselves (intrinsic to the data structure) in an eager or lazy way.;; "Functions passed to alter may depend on the data in the ref"(= 20 (do (dosync (alter the-world (+ % 20)))));; question 16018: My clj-kondo linter tells me that the `do` is redundant. Is this indeed true and if yes why?;; question 16019: Why might a Clojure programmer prefer `partial + 20` rather than `#(+ % 20)` for the above solution?;; question 16020: What are the differences between the current solution and this `alter the-world + 20` ?;; "Two worlds are better than one"(= ["Real Jerry" "Bizarro Jerry"] (do (dosync (ref-set the-world {}) (alter the-world assoc :jerry "Real Jerry") (alter bizarro-world assoc :jerry "Bizarro Jerry") [(the-world :jerry) (bizarro-world :jerry)])));; question 16021: Why are `[(@the-world :jerry) (@bizarro-world :jerry)]` and the same minus the `@` deref prefixes functionally equivalent here? Or are they not but just appear to be?;; question 16022: Where and when is it critical to deref?;; question 16023: What are typical/common gotcha's in Clojure referencing/dereferencing?;; question 16024: What are some more useful/helpful/clear/concrete examples of `do` and `dosync` used in context?Closing Thoughts
TBD...
2021_05_3 Update:
On the theme of doing better, I'd like to continue to update these notebooks. A list of ideas and to-do's:
[ ] Share this and all notebooks in Clojureverse, to facilitate effective collaboration
[ ] Review the clojure.org reference on refs (pun not intended)
[ ] Update the content in this notebook by answering questions & clarifying skills
[ ] Demo some practical use-cases for refs
[ ] Get mentor/export level feedback on what are hard requirements vs "sensible" pre-requisites for learning and using refs in Clojure code
;; alternate solution provided by my friend Naxels(= ["Real Jerry" "Bizarro Jerry"] (do (dosync (ref-set the-world {}) (alter the-world assoc :jerry "Real Jerry") (alter bizarro-world assoc :jerry "Bizarro Jerry") (map :jerry [the-world bizarro-world]))))