vectors, lists, maps and sets

Vectors and maps have a lot in common. They both associate keys with values, the difference being that with vectors the keys are limited to integers while in maps the keys can be more or less anything. That is, in fact, how Clojure looks at vectors—which means that many of the functions that work with maps will also work with vectors. For example, assoc and dissoc work fine on vectors. Thus `(assoc [:title :by :published] 1 :author)` will give you `[:title :author :published]`.

the maps that you create with the literal `{}` or the `hash-map` function make no promises about the order of their keys. There is a second flavor of map that keeps its keys sorted. You can make one of these sorted maps with the aptly named function `sorted-map`.

the order of values returned from `vals` is arbitrary, but it’s guaranteed to match the order of the keys returned by the `keys` function.

Like maps, sets have their own ideas about the order of their elements. The set that you wrote as `#{:sci-fi :romance :mystery}` is liable to come back to you as `#{:sci-fi :mystery :romance}`.

You can create a larger set from an existing set with `conj`; remove elements with `disj`

``````(#{"derby" "h2" "hsqldb" "sqlite"} subprotocol)
``````

this is a test to see if the value bound to `subprotocol` is the name of a database that we recognize

If you’re worried that you may have a set with `nil` as a member, use `contains?` to check for membership

logic

Under the hood, `=` is identical to the Java `equals` method.

The rule is simple: in an `if` statement and any other Boolean context, only `false` and `nil` get treated as `false`. Everything else is treated as `true`.

`cond` takes pairs of expressions, each pair made up of a predicate expression and a value expression

``````(defn shipping-charge [preferred-customer order-amount]
(cond
preferred-customer 0.0
(< order-amount 50.0) 5.0
(< order-amount 100.0) 10.0
:else (* 0.1 order-amount)))
``````

`case`, which based on a single value.

``````(defn customer-greeting [status]
(case status
:gold       "Welcome, welcome, welcome back!!!"
:preferred  "Welcome back!"
"Welcome to Blotts Books"))
``````

If nothing matches, then the expression evaluates to the last, unpaired expression—in this case "Welcome to Blotts Books".

And the easiest way to get hold of an exception value is to use the built-in `ex-info` function:

``````(defn publish-book [book]
(when (not (:title book))
(throw
(ex-info "A book needs a title!" {:book book})))

;; Lots of publishing stuff...
)
``````

The `ex-info` function takes a string describing the problem and a (possibly empty) map containing any other pertinent information.

if you want to catch an exception generated by `ex-info` you will need to look for exceptions of type `clojure.lang.ExceptionInfo`.

multimethod

``````(defn dispatch-book-format [book]
(cond
(vector? book) :vector-book
(contains? book :title) :standard-map
(contains? book :book) :alternative-map))

(defmulti normalize-book dispatch-book-format)

(defmethod normalize-book :vector-book [book]
{:title (first book) :author (second book)})

(defmethod normalize-book :standard-map [book]
book)

(defmethod normalize-book :alternative-map [book]
{:title (:book book) :author (:by book)})
``````

if the dispatch function produces a value for which there is no corresponding defmethod, Clojure will generate an exception. Alternatively, you can supply a method for the value `:default` that will cover the everything else case.

multimethod addition does not have to appear in the same file

pre and post conditions

To set up a `:pre` condition just add a map after the arguments—a map with a `:pre` key. The value should be a vector of expressions.

``````(defn publish-book [book]
{:pre [(:title book) (:author book)]}
(print-book book)
(ship-book book))
``````

You can even take the checking one step further by specifying a `:post` condition, which lets you check on the value returned from the function.

``````(defn publish-book [book]
{:pre  [(:title book) (:author book)]
:post [(boolean? %)]}
(print-book book)
(ship-book book))
``````

functions

One more example of a function-generating function is `every-pred`. It combines predicate functions into a single function that ands them all together.

``````(def cheap-horror-possession?
(every-pred
cheap?
horror?
(fn [book] (= (:title book) "Possession"))))
``````

def

you can get at both the symbol and the value buried inside of the var:

``````(def author "Austen")  ; Make a var.

#'author               ; Get at the var for author -> "Austen".

(def the-var #'author) ; Grab the var.

(.get the-var)         ; Get the value of the var: "Austen"
(.-sym the-var)        ; Get the symbol of the var: author
``````

dynamic vars

there is a Clojure convention for naming dynamic vars. The convention is that dynamic vars should begin and end with `*` (earmuffs)

``````(def ^:dynamic *debug-enabled* false)

(defn debug [msg]
(if *debug-enabled*
(println msg)))

(binding [*debug-enabled* true]
(debug "Calling that darned function")
(some-troublesome-function-that-needs-logging)
(debug "Back from that darned function"))
``````

`set!`, which changes the value of a dynamic var from inside the binding.

``````(set! *print-length* 2)
``````