Reading: Clojure for the Brave – ch9
Concurrency refers to managing more than one task at the same time.
Interleaving is switching between the two tasks (without the first getting to completion).
Parallelism refers to executing more than one task at the same time. It is a subclass of concurrency: before you execute multiple tasks simultaneously, you first have to manage multiple tasks.
It’s important to distinguish parallelism from distribution. Distributed computing is a special version of parallel computing where the processors are in different computers and tasks are distributed to computers over a network.
major use cases for concurrent programming is for blocking operations. If operation is blocking then tasks are executing synchronously, otherwise asynchronously.
Concurrent/Parallel programming is a set of techniques for decomposing a task into subtasks that can execute in parallel and managing the risks that arise when your program executes more than one task at the same time.
When multiple tasks are being executed in parallel or interleaved, there are no guarantees for the overall execution order, so the program is nondeterministic.
Problems to handle: Reference Cells, Mutual Exclusion, and Deadlocks
The reference cell problem occurs when two threads can read and write to the same location, and the value at the location depends on the order of the reads and writes.
Without a way to claim exclusive write access to shared resource, output correctness cannot be guranteed because instructions will be interleaved. So whenever reading/writing a shared resource, Mutual Exclusion should be handled.
Futures, delays, and promises are easy, lightweight tools for concurrent programming.
When you write serial code, you bind together these three events:
Task definition Task execution Requiring the task’s result
Part of learning concurrent programming is learning to identify when these chronological couplings aren’t necessary. Futures, delays, and promises allow you to separate task definition, task execution, and requiring the result.
futures defines a task and places it on another thread, without requiring the result immediately
The future function returns a reference value that you can use to request the result. You can use the reference value to request a future’s result, but if the future isn’t done computing the result, you’ll have to wait.
Requesting a future’s result is called dereferencing the future, and you do it with either the deref function or the @ reader macro.
A future’s result value is the value of the last expression evaluated in its body.
A future’s body executes only once, and its value gets cached.
When you dereference a future, you indicate that the result is required right now and that evaluation should stop until the result is obtained. Hence, dereferencing a future will block if the future hasn’t returned a value.
Delays allow you to define a task without having to execute it or require the result immediately.
You can evaluate the delay and get its result by dereferencing it or by using force.
Like futures, a delay is run only once and its result is cached.
Promises allow you to express that you expect a result without having to define the task that should produce it or when that task should run.
You create promises using promise and deliver a result to them using deliver.
if dereference a promise without first delivering a value, the program would block until a promise was delivered, just like with futures and delays.
You can only deliver a result to a promise once.
One use for promises is to find the first satisfactory element in a collection of data.
For a concurrent program to function correctly, the tasks need to be split into a serial portion and a parallel portion.
Instead of running each task serially, the concurrent parts can run in parallel while coordinating the serial parts (the ones that access a shared resource) to execute in sequence/serially.
Reading: Clojure for the Brave – ch 6
Symbols and Vars
varsare the memory address that contains the value and
symbolsare a human-friendly name for that address
- Given a symbol, clojure tries to find a var corresponding to it in the current namespace
- If you want to use the symbol and not the var it refers to, then you have to quote it (either using
(def books ["b1" "b2"]) ; => #'user/books books ; => ["b1" "b2"]
This code tells Clojure:
- Update the current namespace’s map with the association between
booksand the var.
- Find a free storage in memory and store
["a" "b"]in it
- Write the found address back into the var.
- Return the var (in this case,
This process is called interning a var. You can interact with a namespace’s map of symbols-to-interned-vars using
#'user/great-books is the reader form of a var
(deref #'user/books) tells clojure: ““Get the shelf number from the var, go to that shelf number, grab what’s on it, and give it to me!”
- To use symbols defined in another ns use
(com.microsoft/sql-server)use an object defined in com.microsoft namespace named sql-server
- (clojure.core/refer ‘my-namespace) allows me to use symbols defined in my-namespace without fully-qualifying it.
- When you call
refer, you can also pass it the filters
:rename. As the names imply,
:excluderestrict which symbol/var mappings get merged into the current namespace’s
:renamelets you use different symbols for the vars being merged in.
- (defn- ) defines a private function which isn’t visible to the clients referring to the namespace
(clojure.core/alias 'taxonomy 'cheese.taxonomy)
This code lets us use call symbols from the
cheese.taxonomy namespace with the shorter alias
(require '[the-divine-cheese-code.visualization.svg :as svg]) ;; Equivalent to (require 'the-divine-cheese-code.visualization.svg) (alias 'svg 'the-divine-cheese-code.visualization.svg)
(use 'the-divine-cheese-code.visualization.svg) ;; Equivalent to (require 'the-divine-cheese-code.visualization.svg) (refer 'the-divine-cheese-code.visualization.svg)
Reading: Clojure for the Brave – ch8
- literal forms: things that eval to themselves
- boolean, keys numbers, strings, data-structures (map, list, vector)
- names given to functions, data, macros, etc.
- symbols are resolved by
- is special form? like if
- has local binding (from function-param or let)
- has namespace mapping (using def)
- invalid symbol
- a symbol can point to a function; at parse-time, a symbol and the value it binds to are different even if the symbol is passed around where a function is expected. for example:
(map inc [1 2]):
- The symbol map refers to the map function, but it
shouldn’t be confused with the function itself.
The map symbol is still a data structure, the same
way that the string “fried salad” is a data structure,
but it’s not the same as the function itself
- Special forms
- things that cannot be implemented with functions
- for example with
(if (empty? ) true false)you don’t want to each
element of the outer-most list, hence a special form is needed.
- if, quote are examples
- similarly def, let, loop, fn, do, recur.
- You can place a macro at the beginning of a list, instead of a
symbol/special-form, which allows you to change how rest of the data-structure is evaluated.
- They are executed in between the reader and the evaluator—so they can manipulate the data structures that the reader spits out and transform
with those data structures before passing them to the evaluator.
- the param is not evaluated when passed to macro body; instead a raw list data-structure is sent. This is different from a function body (where it is evaluated.
- Similarly, the return value of a macro IS evaluated, whereas that of a function is NOT.
- You can place a macro at the beginning of a list, instead of a
- You can have overloaded/multi-airity macros just like functions
- Read the sections “Macros All the Way Down” and Double Eval in the same document
- If you need to evaluate values of some sub-expression, you have to quote symbols you are declaring in your macro, otherwise it’ll try to eval a symbol that isn’t created yet. This is done like this:
(defmacro my-print [expression] (list 'let ['result expression] (list 'println 'result) 'result))
- syntax quote
- The syntax is to prefix with ` character, which fully qualifies the quoted symbol
> `+ ; => clojure.core/+ > 'clojure.core/+ ; => clojure.core/+ > '+ ; => +
- You can unquote a single expression inside a syntax-quoted expression
by prefixing with a
~. This is kind of like string-interpolation
> `(+ 1 ~(inc 1)) ; => (clojure.core/+ 1 2)
- Unquote-splicing (
~@) works this way
macro-trials.core> `(+ ~@(list 1 2 3)) (clojure.core/+ 1 2 3) macro-trials.core> `(+ ~(list 1 2 3)) (clojure.core/+ (1 2 3))
First non-trivial PR merged!