<jupyter_start><jupyter_text>Functional things  Functions are values<jupyter_code>;; A map like the ones created in the last chapter, 
;; but with the :price and :genre keywords

(def dracula {:title "Dracula"
              :author "Stoker"
              :price 1.99
              :genre :horror})

;; A series of functions to validate the last map

(defn cheap? [book]
    (when (<= (:price book) 9.99)
        book))

(cheap? dracula)

(defn pricey? [book]
    (when (> (:price book) 9.99)
        book))

(pricey? dracula)

(defn horror? [book]
    (when (= (:genre book) :horror)
        book))

(horror? dracula)

(defn adventure? [book]
    (when (= (:genre book) :adventure)
        book))

(adventure? dracula)

;; The last 4 functions can be generalized as follows

(defn both? [first-predicate-f second-predicate-f book]
    (when (and (first-predicate-f book) (second-predicate-f book))
        book))

(both? cheap? horror? dracula)<jupyter_output><empty_output><jupyter_text>Anonymous functions<jupyter_code>;; A simple anonymous function that doubles its arg

(fn [n] (* 2 n))

;; You can print an anonymous function, like any value

(println "A function:" (fn [n] (* 2 n)))

;; Or bind it to a symbol and call it

(def double-it (fn [n] (* 2 n)))

(double-it 10)

;; The last cheap? function can be expressed in terms a function that 
;; return another function, as follows

(defn cheaper-f [max-price]
    (fn [book]
        (when (<= (:price book) max-price) 
            book)))

;; The last function can be used to create more specific ones, as follows

(def real-cheap? (cheaper-f 1.00))

(real-cheap? dracula)

(def kind-of-cheap? (cheaper-f 1.99))

(kind-of-cheap? dracula)

(def marginally-cheap? (cheaper-f 5.99))

(marginally-cheap? dracula)

;; The last both? function can be expressed in terms of a function 
;; that accepts functions as args and returns another function, as follows

(defn both-f [predicate-f-1 predicate-f-2]
    (fn [book]
        (when (and (predicate-f-1 book) (predicate-f-2 book))
            book)))

;; The last function can be used to create more specific ones, as follows

(def cheap-horror? (both-f cheap? horror?))

(cheap-horror? dracula)

(def real-cheap-adventure? (both-f real-cheap? adventure?))

(real-cheap-adventure? dracula)

(def real-cheap-horror? (both-f real-cheap? horror?))

(real-cheap-horror? dracula)<jupyter_output><empty_output><jupyter_text>Useful built-in functions apply<jupyter_code>;; The 'apply' function takes a function and applies it (hence its name)
;; to a collection of args

(def the-function +)
(def args [1 2 3 4])

(apply the-function args)
;; The apply function is useful for converting from one kind of value to another,
;; as follows

(def v ["The number " 2 " best selling " "book."])

(apply str v)<jupyter_output><empty_output><jupyter_text>partial<jupyter_code>;; A simple function that increments its arg by 1

(defn my-inc [n] (+ 1 n))

;; The last function can be defined in terms of the 'partial' function
;; that can be seen as fixing the 1st arg of '+', as follows

(def my-inc (partial + 1))

;; The bargain-finding functions defined earlier can be expressed
;; in terms of 'partial', as follows

(defn cheaper-than [max-price book]
    (when (<= (:price book) max-price)
        book))

(def real-cheap? (partial cheaper-than 1.00))

(real-cheap? dracula)

(def kind-of-cheap? (partial cheaper-than 1.99))

(kind-of-cheap? dracula)

(def marginally-cheap? (partial cheaper-than 5.99))

(marginally-cheap? dracula)<jupyter_output><empty_output><jupyter_text>complement<jupyter_code>;; Given the following function

(defn adventure? [book]
    (when (= (:genre book) :adventure)
        book))

;; The 'complement' function can be used to create a function
;; that return the oposite value, as follows

(def not-adventure? (complement adventure?))

(not-adventure? dracula)<jupyter_output><empty_output><jupyter_text>every-pred<jupyter_code>;; The 'every-pred' function takes a list of predicates and creates a new predicate 
;: that's the logical AND of the supplied predicates, as follows

(def cheap-horror? (every-pred cheap? horror?))

(cheap-horror? dracula)
(def cheap-horror-possession?
    (every-pred
    cheap?
    horror?
    (fn [book] (= (:title book) "Possession")))) ; An anonymous function can also be used as arg

(cheap-horror-possession? dracula)<jupyter_output><empty_output><jupyter_text>Function literals (lambdas)<jupyter_code>;; The last adventure? function rewritten as function literal

#(when (= (:genre %1) :adventure) %1) ; %1 means 1st arg, %2 means 2nd arg, etc.

;; Instead of using 'partial', a function that doubles its arg
;; can be expressed as follows

#(* 2 %1)

;; For 1-argument functions, the syntax can be shortened even more, as follows

#(* 2 %) ; No need to use '%1', '%' is enough

(def book {:title "Emma" :copies 1000})

book
(def new-book (update book :copies inc))

new-book
(def by-author
    {:name "<NAME>"
     :book {:title "Emma" :copies 1000}})

by-author
(def new-by-author (update-in by-author [:book :copies] inc))

new-by-author<jupyter_output><empty_output><jupyter_text>Example usage of the last functions in the 'ring' library<jupyter_code>;; A simple function that takes an HTTP request and returns
;; an HTTP response, as follows

(defn handler [request]
    {:status 200
     :headers {"Content-Type" "text/html"}
     :body "Hello from your web application!"})

;; Adding logging to the last functions, as follows

(defn log-value
    "Log the message and the value. Returns the value."
    [msg value]
    (println msg value)
    value)

(defn wrap-logging
    "Return a function that logs the response."
    [msg handler]
    (fn [request] (log-value msg (handler request))))

;; Modifying the 'content-type' HTTP field, as follows

(defn wrap-content-type
    "Return a function that sets the response content type."
    [handler content-type]
    (fn [request]
        (assoc-in
            (handler request)
            [:headers "Content-Type"]
            content-type)))

;; The last functions can be used in the main app, as follows

(def app
    (wrap-logging "Final response:" (wrap-content-type handler "text/html"))) ; Logging initial response

(def app
    (wrap-content-type (wrap-logging "Initial response:" handler) "text/html")) ; Logging final response

(def app
    (wrap-logging "Final response:" 
    (wrap-content-type (wrap-logging "Initial response:" handler) "text/html")))  ; Logging both<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12714723926380367, 'fraction_numerical': 0.010429447852760737, 'mean_word_length': 3.8447251114413077, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '458037', 'license': 'MIT', 'n_tokens_mistral': 2259, 'n_tokens_neox': 2164, 'n_words': 758, 'path': 'chapter06/chapter06.ipynb', 'repo_name': 'will-i-amv-books/getting-clojure', 'size': '6525'}