Skip to main content

Functions & Bindings

Progress 0/11

Functions and names are the bread and butter of any Phel program. You'll learn to bind values, define functions, scope locals, destructure inputs, and build closures that remember.

Exercise 1 easy

Use def to bind the name greeting to "Hello, Phel!". Then evaluate greeting.

(def greeting "Hello, Phel!")
greeting
; => "Hello, Phel!"

def creates a global binding - a name pointing at a value.

Learn more: Global and Local Bindings

Exercise 2 easy

Use defn to define a function hello that takes no arguments and returns "hello!".

(hello) ; => "hello!"
(defn hello [] "hello!")

defn is short for "define function". The empty [] is the parameter list; the last expression in the body is the return value.

Learn more: Functions and Recursion

Exercise 3 easy

Define double so that (double 5) returns 10.

(defn double [n] (* n 2))

Learn more: Functions and Recursion

Exercise 4 easy

Add a docstring to double. Then look it up with (doc double).

(defn double
  "Multiplies the given number by 2."
  [n]
  (* n 2))
(doc double)

Docstrings sit between the name and the parameter list. doc reads them back at the REPL - your future self will thank you.

Learn more: Functions and Recursion

Exercise 5 easy

Use let to bind name to "world", then return "Hello, world!" via str.

(let [name "world"]
  (str "Hello, " name "!"))
; => "Hello, world!"

let creates locals that exist only inside its body. Use it to keep your scope tight and your global namespace clean.

Learn more: Global and Local Bindings

Exercise 6 medium

Use let with multiple bindings to compute the area of a rectangle (width 5, height 3).

(let [width 5
      height 3]
  (* width height))
; => 15

You can stack as many bindings as you need. Later bindings can refer to earlier ones.

Learn more: Global and Local Bindings

Exercise 7 medium

Pull x and y out of the vector [10 20] in a single let, then return their sum.

(let [[x y] [10 20]]
  (+ x y))
; => 30

This is destructuring: the binding form mirrors the shape of the value. It also works with maps:

(let [{:keys [name age]} {:name "Ada" :age 36}]
  (str name " is " age))
; => "Ada is 36"

Learn more: Destructuring

Exercise 8 medium

Create an anonymous function that adds 10 to a number. Call it with 5. Try both the fn form and the short #() form.

((fn [x] (+ x 10)) 5)
; => 15

(#(+ % 10) 5)
; => 15

Anonymous functions shine when you need a one-off (think map, filter). The #() shortcut uses % for the first argument, %2 for the second, and so on.

Learn more: Functions and Recursion

Exercise 9 medium

Define greet with an optional second argument that defaults to "Hello":

(greet "Ada")           ; => "Hello, Ada!"
(greet "Ada" "Welcome") ; => "Welcome, Ada!"
(defn greet
  [name & [greeting]]
  (let [g (or greeting "Hello")]
    (str g ", " name "!")))

The & [greeting] destructures any extra arguments. or substitutes a default when greeting is nil.

Learn more: Functions and Recursion, Destructuring

Exercise 10 medium

Build make-adder so that (make-adder n) returns a function that adds n to its argument:

(def add5 (make-adder 5))
(add5 10) ; => 15
(add5 20) ; => 25
(defn make-adder [n]
  (fn [x] (+ x n)))

(def add5 (make-adder 5))
(add5 10) ; => 15

make-adder returns a fresh function that closes over n - this is a closure. The captured n lives on inside the returned function for as long as you keep it.

Learn more: Functions and Recursion

Exercise 11 medium

Implement factorial using recursion.

(factorial 5) ; => 120
(defn factorial [n]
  (if (<= n 1)
    1
    (* n (factorial (dec n)))))

Classic recursion: shrink the problem on every call until you hit the base case (n <= 1). For huge n you'd reach for loop/recur (next section) to avoid blowing the stack.

Learn more: Functions and Recursion