After a good day on yesterday (Chapter 4 of Clojure from the Ground Up, its exercises and 16 4clojure problems), the expectation for today is more limited as I am constrained by time. I expect to read part of Chapter 6 (skipping Chapter 5, macros, for now as it's not that relevant to the Athens codebase) Whatever I can achieve will be a little victory.
Declaration with let can be used outside of their original expressions as illustrated by this example:
user=> (let [x 1]
(prn (inc x))
(prn (inc x)))
2
2
I was happy to see that closure's, like in JavaScript, also made it to Clojure. Functions will remember the symbols from the time they were constructed. They will reach out to their lexical scope like in this example:
(defn present
[gift]
(fn [] gift))
user=> (def red-box (present "plush tiger"))
#'user/red-box
user=> (red-box)
"plush tiger"
In this case, present
creates a new function that will always reach for the value of gift, passed at the function creation.
It is possible to delay a function's execution, by using let
. (def later (fn [] (prn "Adding") (+ 1 2)))
will only return 3
when the function is called like this (later)
.
There's even a standard macro for this called delay
that you can use like this (def later (delay (prn "Adding") (+ 1 2)))
and it has to be called with defer
, like so (deref later)
, or with the shorthand @later
.
Futures delegates computation to a different thread, to be evaluated when possible. It's evaluated in parallel. Futures return immediately and give us the identity which will point to the value of the last expression in the future.
user=> (def x (future (prn "hi") (+ 1 2)))
"hi"
#'user/x
user=> (deref x)
3
Evaluation of threads is concurrent and it is possible that expressions will be evaluated out of order. To quote the author, "Futures are the most generic parallel construct in Clojure. You can use futures to do CPU-intensive computation faster, to wait for multiple network requests to complete at once, or to run housekeeping code periodically." This is something that I have not really seen before, coming from JavaScript. The closest in JavaScript would probably using web workers (in the browser).
You can defer execution, pass a value, and then deref it. It guarantees that any attempt to read the value will wait until the value has been written. We can use promises to synchronize a program which is being evaluated concurrently.
A simple example of promise
would go like this:
(def box (promise))
(deliver box :maceo)
(deref box) ; returns :maceo
There was an interesting example using a promise
and a future
, like this:
(def card (promise))
(def dealer (future
(Thread/sleep 5000)
(deliver card [(inc (rand-int 13))
(rand-nth [:clubs :spades :hearts :diamonds])])))
It indeed delivered on that promise of delivering the... promise. With a (deref card) you fill it and delegate it to another thread. In summary:
- delays defer evaluation
- futures parallelize it
- promises are concurrent "without specifying how the evaluation occurs", we provide the value, when we have it
So we covered vars back on Tuesday. As a reminder, vars are mutable variables and use the def
keyword to be created. They can be updated.
(def x :mouse)
(def box (fn [] x))
(def x :cat)
(box) ; will return :mouse
A reference is the same everywhere and is called a global variable.
A dynamic var reference can only be overridden in one particular scope. The convention is to name them with asterisks around their names, like so (def ^:dynamic *board* :maple)
.
(def ^:dynamic *board* :maple)
(defn cut [] (prn "sawing through" *board*))
(cut) ; "sawing through" :maple
(binding [*board* :cedar] (cut)) ; will return "sawing through" :cedar, this is specific to this scope
(cut) ; otherwise the value remains the same => "sawing through" :maple
Within the binding
expression the value of *board*
has changed, but it remains the same outside of it.
fn
and let
create immutable lexical scope, binding creates a dynamic scope. The dynamic scope propagates through function calls when the lexical scope is limited to the literal text of the fn
or let
.
When writing actual code, one should use def
sparingly.
Today I did quite a lot with the time that I had. I had a good introduction to concurrency in Clojure with vars, delay
, future
and promises
. I am looking forward to learning about atoms and refs tomorrow.
Let's end the day with a tally of what I completed (which is much more than I anticipated!):
- Most of Chapter 6 of Clojure From the Group Up
- 3 4clojure problems
- Still wrote more ~800 words, only 200 less than previous days