This is a study note of the part II of the book Getting Clojure.

Ch10: Sequence

Clojure uses a wraper method to define a set of standard functions sequence type.

  • (seq col) returns a sequnce, nil for an empty collection.
  • A universal interface: first, rest, next, cons
  • For empty collection, rest returns an empty sequence; next retruns nil.
  • Toolkit: sort, reverse, partition, interleave, interpose.
  • Richer functions: filter, some, map, comp, for, reduce, sort-by.
  • Other sources: line-seq, re-seq.
  • ->> put a result at the end of teach step.
  • -> put a result at the front of each step.
  • conj appends a vector and preappends a list but return the orginal collection.
  • cons prepends a ventor or a list and returns a sequence.

Ch11: Lazy Sequence

Laziness can save cycles and organize programs (like the REPL). Sequences without end, such as repeat, repeatedly, cycle, and iterate must be lazy.

Lazy functions: take, map, interleave, interpose, cons.

Behind the scenes, lazy-seq uses Macro to wrap its argument in an anonymous function.

Use doall and doseq run down lazy sequnce and access elements for all or one by one.

Don’t uses count, sort, reduce with unbounded sequences.

Ch12: Destructuring

  • Destructuring works with let and function parameters.
  • Take from the start or skip using _.
  • Use [] to delimit any sequential data type.
  • Use [{symbol :keyword}] to desstructure a map.
  • Use `[{:keys [symbol1 symbol2]}] to get values for selected keywords.
  • Use :as symbol to fetch the original value.
  • Use :or for missing values.

Ch13: Records and Protocols

Map is slow and too generic. Records are maps with predefined keys and functions working with maps also work with records.

The benefits of records are 1) providing types 2) better performance.

Protocols defines an interface that has a set of methods. The protocol is independent defined – this is a desired feature because it decouples data and functions. It can be defined independently for any type using extend-protocol. Better to put each protocol in its own namespace to avoid function name confliction.

Comparison with multimethods:

  • single function vs. an interface with a set of functions.
  • dispatch on a record type vs. arbitrary dispatch mechanism.

Use reify to define one-off protocol implementation.

Ch14: Tests

  • deftest and testing to define a set and subset of tests.
  • Propety-base testing generates tests for checking properties.

Ch15: Spec

  • clojure.spec validate the shape, not type of data.
  • A spec specifies a pattern.
  • Functions: s/and, s/or, s/coll-of, s/cat, s/keys, s/?.
  • Use s/def to register and s/valid to run.
  • s/explain and s/conform explians why.
  • s/fdef defines function spec.
  • test.check enables spec-driven tests.