Search

May 20, 2020

Clojure Goodness: Composing Functions With comp

In Clojure we can use the comp function to create a new function based on multiple other functions. We create a new function by composing other functions. We can invoke the function that is returned from the comp function with input arguments. These arguments are applied to the right most function that was used with comp. Then the output of that function is the input for the next function.

In the following example code we see several usages of the comp function. Also we see how the ordening of the functions can change the output:

(ns mrhaki.core.comp
  (:require [clojure.test :refer [is]]
            [clojure.string :as str]))

;; Some functions that work with numbers.
(defn f [x] (+ 11 (- x 90)))
(defn g [x] (* 2 x))

;; Use comp to create a new function based
;; on functions f and g.
;; Function are applied from right to left,
;; so first f and then g.
(is (= 42 ((comp g f) 100)))

;; Notice ordering is important.
;; In this case first g, then f is applied.
(is (= 121 ((comp f g) 100)))


;; User data to work with.
(def user {:name "Hubert" :alias "mrhaki"})

;; Simple function to repeat the value twice.
(def duplicate (partial repeat 2))

;; Compose new function from functions
;; :alias (keyword function), 
;; str/capitalize and
;; duplicate.
;; Function are applied from right to left.
(is (= '("Mrhaki" "Mrhaki")
       ((comp duplicate str/capitalize :alias) user)))

;; Other alternatives to chaining functions
(is (= '("Mrhaki" "Mrhaki")
       ;; using an anonymous function
       (#(duplicate (str/capitalize (:alias %1))) user)
       ;; or thread macro
       (-> user :alias str/capitalize duplicate)
       ;; or simply nested functions.
       (duplicate (str/capitalize (:alias user))))) 

Written with Clojure 1.10.1.