Part I. The language · this page as markdown

Functions

You have been using words the language gives you: inc, upper, count. A function is a word of your own. This chapter makes them, passes them around, and lets them call themselves.

fn #

fn makes a function: a list of names for what it takes, and a form that works out what it gives. Put one first in a list to call it:

#fn-call
((fn [n] (* n n)) 7)
It answers
49

That's a function of one value, n, giving n times n, called on 7. More often a function is handed to a word that calls it, as map did in the last chapter:

#fn-map
(map (fn [n] (* n n)) [1 2 3])
(filter (fn [w] (starts-with? w "@")) ["@mira" "tea" "@cairn"])
It answers
(1 4 9)
("@mira" "@cairn")

A function takes up to six values.

The short way: #( ) #

For a small function, #(…) is shorter: % is the value it takes, or %1, %2 when it takes more than one:

#fn-short
(map #(* % %) [1 2 3])
(map #(str %1 " " %2) ["3" "5"] ["roses" "asters"])
It answers
(1 4 9)
("3 roses" "5 asters")

Use #() when the whole function fits on a line and is easy to read; fn with names when it isn't.

defn #

defn gives a function a name, at the top of a plan, for the rest of the plan to use:

#fn-defn
(defn rings-text [n]
  (if (= n 1) "1 ring" (str n " rings")))
(rings-text 1)
(rings-text 4)
(map rings-text [1 2 3])
It answers
"1 ring"
"4 rings"
("1 ring" "2 rings" "3 rings")

A name given by defn lasts for the plan it's in. The next plan starts afresh.

One rule to remember: at the top of a plan, a function you call by name comes from defn. A let at the top can hold a function and hand it to map, but can't call it by name:

Refused#fn-let-top
(let [twice (fn [n] (* 2 n))]
  (twice 21))
It answers
Step 2, value, op call's fn "twice" isn't a function this plan defined before it, or a name given before it here. Nothing was changed.

Write (defn twice [n] (* 2 n)) instead. Inside a toy it is the other way round: a toy's code has no defn, and a let inside it holds its functions and calls them by name (chapter 9).

Functions that make functions #

A few words take functions and give new ones. partial fills in the first values a function takes; comp joins functions into one, the last applied first; complement turns a test round; constantly always gives one value; juxt gives a list of several functions' answers; fnil gives a value in place of nil:

#fn-makers
((partial + 10) 5)
((comp upper trim) "  ting  ")
((complement even?) 3)
((constantly "Ting.") "anything")
((juxt count upper) "bell")
((fnil inc 0) nil)
It answers
15
"TING"
true
"Ting."
[4 "BELL"]
1

A function that calls itself #

A function can call itself, on a smaller problem each time, until the problem is small enough to answer straight away:

#fn-recursion
(defn countdown [n]
  (if (= n 0)
    ["off!"]
    (cons n (countdown (dec n)))))
(countdown 3)
It answers
(3 2 1 "off!")

(countdown 3) is 3 in front of (countdown 2), which is 2 in front of (countdown 1), and so on down to (countdown 0), which answers without calling itself. Every such function needs a case like that, or it never ends. One that never ends is stopped, and nothing it did is kept:

Refused#fn-forever
(defn forever [n] (forever (inc n)))
(forever 0)
It answers
That recursed too deeply. Nothing was changed.

loop and recur #

loop is a function and its first call in one. It names values, like a let, and recur starts it again with new ones. This adds up 1 to 10:

#fn-loop
(loop [n 1
       total 0]
  (if (> n 10)
    total
    (recur (inc n) (+ total n))))
It answers
55

A loop can go round many more times than a function can call itself, because recur doesn't build up a chain of calls. Every round is a step of the plan's fuel, though, so a loop with no way out is stopped in the end (chapter 17).

Most of the time you won't need either: map, filter and reduce say what you mean more plainly. Reach for loop when each round depends on the last in a way they don't fit.

A function as a value #

A function is a value like any other: it can be named, put in a list or a map, and handed on. This keeps a small table of greetings by the time of day:

#fn-values
(let [greetings {:dawn (fn [who] (str "Early, " who "!"))
                 :night (fn [who] (str "Hush, " who "."))}
      greet (get greetings :night)]
  (map greet ["Mira" "Cairn"]))
It answers
("Hush, Mira." "Hush, Cairn.")

Try it #

  1. Write (defn shout [t] …) that answers t in capitals with "!" after it, and use it on "ting".
  2. With #(), answer each number of [1 2 3] plus 100.
  3. Write (defn sum-list [l] …) that adds up a list by calling itself on its rest. Then do the same with reduce.
  4. With loop, answer the first power of 2 over 1,000.

Recap #

Next: chance, and why Plazmi's always repeats.

Written by Claude, Plazmi's writer, for Plazmi, a service of Manatee Media LLC. Text © 2026 Manatee Media LLC. The examples are free to copy, change and run.