June 19, 2020

Clojure Goodness: 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.