Core

There Is Truth In Many Things

In Clojure there are only two things false in a boolean or conditional context: false and nil. All other values are threated as true. A value that is false in a boolean context is also called falsey, and a value that is true is called truthy.

In the following example we use false and nil as falsey and several other values as truthy values:

(ns mrhaki.core.truthy-falsey
  (:require [clojure.test :refer [is]]))

;; Only nil and false are falsey.
(is (= false (boolean false)))
(is (= false (boolean nil)))
(is (= false (boolean ({:cool "Clojure"} :language))))

(is (= :falsey (if nil :truthy :falsey)))

;; Other values are truthy.
(is (= true (boolean true)))
(is (= true (boolean "mrhaki")))
(is (= true (boolean 0)))
(is (= true (boolean 1)))
(is (= true (boolean '())))
(is (= true (boolean [1 2 3])))
(is (= true (boolean {})))
(is (= true (boolean ({:rocks "Clojure"} :rocks))))

(is (= :truthy (if "Clojure" :truthy :falsey)))
(is (= :truthy (if "" :truthy :falsey)))
(is (= :empty-but-truthy (if [] :empty-but-truthy :empty-and-falsey)))

Written with Clojure 1.10.1.

Original post written on October 5, 2020

Get Clojure Version

To get the current Clojure version we must use the clojure-version function. The function simply returns the Clojure version we are using from our code.

In the following example we simply check the result of clojure-version and also define a function to get the Javaa version:

(ns mrhaki.core.version
  (:require [clojure.test :refer [is]]))

;; Function clojure-version returns Clojure version.
(is (= "1.10.1" (clojure-version)))

(defn java-version
  "Returns Java version as printable string."
  []
  (System/getProperty "java.version"))

(is (= "14" (java-version)))

Written with Clojure 1.10.1.

Original post written on April 6, 2020

Keyword As Function

In Clojure functions are everywhere. In a previous post we learned that sets can be functions, but Clojure also makes keywords functions. A keyword is a symbol starting with a colon (:) and is mostly used in map entries as key symbol. The keyword as function accepts a map as single argument and returns the value for the key that equals the keyword in the map or nil if the keyword cannot be found.

In the following code we use keywords as function in several examples:

(ns mrhaki.core.keyword-function
  (:require [clojure.test :refer [is]]))

;; Sample map to use in examples.
(def user {:name "Hubert" :alias "mrhaki" :living {:country "Netherlands"}})


;; Keyword is a function with a map argument and
;; returns value for keyword in the map.
(is (= "mrhaki" 
       (:alias user)
       ;; We get the same result with get.
       (get user :alias)))

(is (= {:country "Netherlands"} (:living user)))

(is (= "Netherlands"
       (:country (:living user))
       (-> user :living :country)
       ;; We can use get-in to get values from nested maps.
       (get-in user [:living :country])))


;; When keyword is not in the map we get a nil result.
(is (nil? (:city user)))
(is (= "not-found" (or (:city user) "not-found")))


;; Works also for namespaced keywords.
(is (= "mrhaki" (:user/alias {:name/full-name "Hubert" :user/alias "mrhaki"})))


;; Using keyword as function with juxt.
(is (= ["mrhaki" "Hubert"] 
       ((juxt :alias :name) user)))

Written with Clojure 1.10.1.

Original post written on June 25, 2020

Using Sets As Functions

One of the nice things in Clojure is that some data structures are also functions. For me this felt rather strange when learning Clojure (coming from Java), but it can be very powerful. A set in Clojure is also a function. The set as function accept a single argument and it return nil when the argument is not part of the set, otherwise the argument value is returned. This behaviour also makes a set as function a nice predicate to be used for example in collection functions.

In the following example code we use different sets as function:

(ns mrhaki.set.function
  (:require [clojure.test :refer [is]]))

;; Sample set with some JVM languages.
(def languages #{"Clojure" "Groovy" "Kotlin"})

;; We can use the set languages as function
;; with one argument to check if the argument
;; is part of the set.
(is (= "Clojure" (languages "Clojure")))

;; If the argument is not part of the set
;; we get back nil.
(is (= nil (languages "Java")))


;; As nil is logical false in Clojure 
;; a set makes a nice predicate.
(is (= ["Clojure"] (filter #{"Clojure" "Java"} languages)))
(is (= ["Kotlin" "Groovy"] (remove #{"Java" "Clojure"} languages)))


;; Sample vector with numbers.
(def numbers [0 2 1 4 2 3 1 0])

;; Use set as predicate.
(is (= [2 1 2 1] (filter #{1 2} numbers)))

;; As set #{1 2} is a function we can use it as argument
;; for other functions to create a new function.
(is (= [0 4 3 0] (filter (complement #{1 2}) numbers)))

Written with Clojure 1.10.1.

Original post written on June 19, 2020

Destructure Sequences

Clojure supports advanced destructure features. In a previous post we learned about destructuring maps, but we can also destructure vectors, list and sequences in Clojure using positional destructuring. We can define symbols for positions in the sequence to assign the value at a certain position to the symbol. The first symbol in the destructure vector gets the value of the first element in the sequence, the second symbol the value of the second element and so on. To get the remaining elements from the sequence without assigning them to specific symbols we can use & followed by a symbol. Then all remaining elements are assigned as sequence the symbol. Finally we can use :as to get the original vector, list or sequence.

The folowing examples show several destructure definitions for different type of collections and sequences:

(ns mrhaki.lang.destruct-seq
  (:require [clojure.test :refer [is]]))

(def items ["mrhaki" "Hubert Klein Ikkink" "Tilburg"])

;; Elements from the items vector are positionally
;; destructured to symbols.
(let [[alias name city] items]
  (is (= "mrhaki" alias))
  (is (= "Hubert Klein Ikkink" name))
  (is (= "Tilburg" city)))

;; When we define a symbol but there are no elements
;; to assign a value, the symbol will be nil.
(let [[alias name city country] items]
  (is (nil? country)))

;; When we don't need the destructured symbol we can
;; use the underscore to indicate this. But any name will do.
(let [[username _ city] items]
  (is (= "mrhaki lives in Tilburg" 
         (str username " lives in " city))))

;; We can destructure sequences just like vectors.
(def coords '(29.20090, 12.90391))

(let [[x y] coords]
  (is (= 29.20090 x))
  (is (= 12.90391 y)))

(let [[first-letter _ third-letter] "mrhaki"]
  (is (= \m first-letter))
  (is (= \h third-letter)))


;; We can nest our destructure definitions.
(def currencies [[42 "EUR"] [50 "USD"]])

;; We want the second value of the first element and
;; the first value of the second element.
(let [[[_ currency] [amount _]] currencies]
  (is (= "EUR" currency))
  (is (= 50 amount)))

;; Example sequence with fruit names.
(def basket '("Apple" "Pear" "Banana" "Grapes" "Lemon"))

;; We can use & to assign all remaining not-yet 
;; destructured element to a sequence.
(let [[first second & rest] basket]
  (is (= "Apple" first))
  (is (= "Pear" second))
  (is (= ["Banana" "Grapes" "Lemon"] rest)))

;; We can use :as to get the original sequence.
(let [[first _ third :as fruits] basket]
  (is (= "Apple" first))
  (is (= "Banana" third))
  (is (= "APBGL" (apply str (map #(.charAt % 0) fruits)))))


;; Use destructure in function parameter to 
;; destructure the argument value when invoked.
(defn summary
  [[first second :as all]]
  (str first ", " second " and " (- (count all) 2) " more fruit names."))

(is (= "Apple, Pear and 3 more fruit names."
       (summary basket)))

Written with Clojure 1.10.1.

Original post written on February 9, 2021

Destructuring Maps

When we want to assign key values in a map to symbols we can use Clojure's powerful destructure options. With destructuring a map we can use dense syntax to assign keys to new symbols. For example we can use that in a let special form to assign symbols, but also for function parameters that are a map. When we use it for function parameters we can immediately assign keys to symbols we want to use in the function. Clojure provides a simple syntax to destructure a key value to a symbol using {symbol key} syntax. The value of :key will be assigned to symbol. We can provide default values if a key is not set in the map using :or followed by the symbol and default value. This is very useful if we know not all keys in a map will have values. Finally there is a shorthand syntax to assign keys to symbols with the same name as the key: :keys. We must provide a vector to :keys with the name of the keys, which will automatically assigned to symbols with the same name. To use this destructuring to its fullest the keys in the map must be keywords. We can use the keywordize-keys function in the clojure.walk namespace if we have a map with string keys and we want to transform them to keywords.

In the following example code we see several example of map destructuring:

(ns mrhaki.lang.destruct-map
  (:require [clojure.test :refer [is]]))

;; Sample map structure we want to destructure.
(def user {:first-name "Hubert"
           :last-name  "Klein Ikkink"
           :alias      "mrhaki"})

;; We can define a symbol username that will have the
;; the value of the :alias key of the user map.
(let [{username :alias} user]
  (is (= "mrhaki" username)))

;; When we use a non-existing key the symbol will
;; have a nil value, like the symbol city in the 
;; following example.
(let [{username :alias city :city} user]
  (is (nil? city))
  (is (= "mrhaki" username)))

;; We can use :or to define a value when a key
;; is not available in the map.
;; Here we define "Tilburg" as default value if
;; the :city key is missing from the map.
(let [{username :alias city :city :or {city "Tilburg"}} user]
  (is (= "Tilburg" city))
  (is (= "mrhaki" username)))

;; The symbol names must match in the definition
;; for the key value and the :or value.
(let [{username :alias lives-in :city :or {lives-in "Tilburg"}} user]
  (is (= "Tilburg" lives-in))
  (is (= "mrhaki" username)))

;; We can use :as to assign the original map
;; to a symbol, that we can use in the code.
(let [{username :alias :as person} user]
  (is (= "Hubert" (:first-name person)))
  (is (= "Klein Ikkink" (:last-name person)))
  (is (= "mrhaki" username)))

;; If the symbol name matches the key name we
;; can use :keys to define that so we have to type less.
(let [{:keys [alias first-name last-name]} user]
  (is (= "mrhaki" alias))
  (is (= "Hubert" first-name))
  (is (= "Klein Ikkink" last-name)))

;; Combination of destruturing options for a map.
(let [{:keys [first-name last-name city]
       :or   {city "Tilburg"}
       :as   person} user]
  (is (= "Hubert" first-name))
  (is (= "Klein Ikkink" last-name))
  (is (= "Tilburg" city))
  (is (= "mrhaki" (:alias person))))


;; Use destructuring in a function argument.
(defn who-am-i
  [{:keys [first-name last-name city]
    :or   {city "Tilburg"}
    :as   person}]
  (str first-name " " last-name ", aka " (:alias person) ", lives in " city))

(is (= "Hubert Klein Ikkink, aka mrhaki, lives in Tilburg"
       (who-am-i user)))
       
       
;; Another map with string keys. 
(def string-map {"alias" "mrhaki" "city" "Tilburg"})

(let [{username "alias" city "city"} string-map]
  (is (= "mrhaki" username))
  (is (= "Tilburg" city)))

;; We can use :strs instead of :keys for string keys.
(let [{:strs [alias city]} string-map]
  (is (= "mrhaki" alias))
  (is (= "Tilburg" city)))

;; Or convert string keys to keywords.
(let [{:keys [alias city]} (keywordize-keys string-map)]
  (is (= "mrhaki" alias))
  (is (= "Tilburg" city)))


;; For completeness we can destructure symbol keys.
(def sym-map {'alias "mrhaki" 'name "Hubert Klein Ikkink"})

(let [{username 'alias} sym-map]
  (is (= "mrhaki" username)))

;; We can use :str instead of :keys.
(let [{:syms [alias name]} sym-map]
  (is (= "mrhaki" alias))
  (is (= "Hubert Klein Ikkink" name)))

Written with Clojure 1.10.1.

Original post written on February 9, 2021