Clojure Functions
Functions are the building blocks of Clojure programs. Clojure provides powerful tools for creating and composing functions in a functional programming style.
Defining Functions
Basic Function Definition
(defn greet
"Returns a greeting string"
[name]
(str "Hello, " name "!"))
(greet "Alice") ; => "Hello, Alice!"
Multi-arity Functions
(defn greet
"Returns a greeting string"
([] (greet "World"))
([name] (str "Hello, " name "!")))
(greet) ; => "Hello, World!"
(greet "Bob") ; => "Hello, Bob!"
Variadic Functions
(defn sum
"Sums all arguments"
[& numbers]
(apply + numbers))
(sum 1 2 3) ; => 6
Anonymous Functions
fn Form
(def increment (fn [x] (+ x 1)))
(increment 5) ; => 6
Short Syntax
(#(* % 2) 5) ; => 10 (double)
(#(str %1 " and " %2) "salt" "pepper") ; => "salt and pepper"
Higher-Order Functions
map
(map inc [1 2 3]) ; => (2 3 4)
(map #(* % %) [1 2 3]) ; => (1 4 9)
filter
(filter even? [1 2 3 4]) ; => (2 4)
(filter #(> % 2) [1 2 3 4]) ; => (3 4)
reduce
(reduce + [1 2 3 4]) ; => 10
(reduce * 2 [1 2 3]) ; => 12 (with initial value 2)
complement
(filter (complement even?) [1 2 3 4]) ; => (1 3)
Function Composition
comp
(def inc-and-double (comp #(* % 2) inc))
(inc-and-double 5) ; => 12
partial
(def add5 (partial + 5))
(add5 10) ; => 15
juxt
((juxt + * min max) 2 3) ; => [5 6 2 3]
Recursion
Simple Recursion
(defn factorial
[n]
(if (<= n 1)
1
(* n (factorial (dec n)))))
(factorial 5) ; => 120
Tail Recursion with recur
(defn factorial
[n]
(loop [count n acc 1]
(if (zero? count)
acc
(recur (dec count) (* acc count)))))
(factorial 5) ; => 120
Multimethods
(defmulti area :shape)
(defmethod area :circle
[{:keys [r]}]
(* Math/PI r r))
(defmethod area :rectangle
[{:keys [w h]}]
(* w h))
(area {:shape :circle :r 5}) ; => 78.539...
(area {:shape :rectangle :w 2 :h 3}) ; => 6
Protocols
(defprotocol Greeter
(greet [this]))
(defrecord English []
Greeter
(greet [this] "Hello!"))
(defrecord Spanish []
Greeter
(greet [this] "¡Hola!"))
(greet (->English)) ; => "Hello!"
(greet (->Spanish)) ; => "¡Hola!"
Function Metadata
(defn ^{:doc "Adds two numbers" :added "1.0"} add
[x y]
(+ x y))
(meta #'add) ; => {:doc "Adds two numbers", :added "1.0", ...}
Pre/Post Conditions
(defn constrained-sqrt
[x]
{:pre [(pos? x)]
:post [(> % 0)]}
(Math/sqrt x))
(constrained-sqrt 25) ; => 5.0
(constrained-sqrt -25) ; => AssertionError
Memoization
(defn fib [n]
(if (< n 2)
n
(+ (fib (dec n)) (fib (- n 2)))))
(def memo-fib (memoize fib))
(time (fib 35)) ; => "Elapsed time: 1000 ms"
(time (memo-fib 35)) ; First call: "Elapsed time: 1000 ms"
(time (memo-fib 35)) ; Subsequent calls: "Elapsed time: 0.1 ms"
Function Patterns
Function Factories
(defn make-adder
[x]
(fn [y] (+ x y)))
(def add5 (make-adder 5))
(add5 10) ; => 15
Closures
(defn make-counter
[]
(let [count (atom 0)]
(fn []
(swap! count inc))))
(def c (make-counter))
(c) ; => 1
(c) ; => 2
Higher-order Helpers
(defn apply-twice
[f x]
(f (f x)))
(apply-twice #(* % 2) 5) ; => 20
Performance Considerations
- Primitive type hints: Use ^long, ^double etc. for performance-critical numeric functions
- Transducers: For efficient sequence processing (covered in Sequences section)
- Inlining: Use ^:inline metadata for small functions
Next Steps
Continue learning with:
- Clojure Sequences - Working with lazy sequences
- Macros - Extending the language with macros
- Concurrency - Functional approaches to concurrency