;; name params body
;; ----- ------ -------------------
(defn greet [name] (str "Hello, " name) )
Clojure is a functional language. Functions are first-class and can be passed-to or returned-from other functions. Most Clojure code consists primarily of pure functions (no side effects), so invoking with the same inputs yields the same output.
defn
defines a named function:
;; name params body
;; ----- ------ -------------------
(defn greet [name] (str "Hello, " name) )
This function has a single parameter name
, however you may include any number of parameters in the params vector.
Invoke a function with the name of the function in "function position" (the first element of a list):
user=> (greet "students")
"Hello, students"
Functions can be defined to take different numbers of parameters (different "arity"). Different arities must all be defined in the same defn
- using defn
more than once will replace the previous function.
Each arity is a list ([param*] body*)
. One arity can invoke another. The body can contain any number of expressions and the return value is the result of the last expression.
(defn messenger
([] (messenger "Hello world!"))
([msg] (println msg)))
This function declares two arities (0 parameters and 1 parameter). The 0-parameter arity calls the 1-parameter arity with a default value to print. We invoke these functions by passing the appropriate number of arguments:
user=> (messenger)
Hello world!
nil
user=> (messenger "Hello class!")
Hello class!
nil
Functions may also define a variable number of parameters - this is known as a "variadic" function. The variable parameters must occur at the end of the parameter list. They will be collected in a sequence for use by the function.
The beginning of the variable parameters is marked with &
.
(defn hello [greeting & who]
(println greeting who))
This function takes a parameter greeting
and a variable number of parameters (0 or more) that will be collected in a list named who
. We can see this by invoking it with 3 arguments:
user=> (hello "Hello" "world" "class")
Hello (world class)
You can see that when println
prints who
, it is printed as a list of two elements that were collected.
An anonymous function can be created with fn
:
;; params body
;; --------- -----------------
(fn [message] (println message) )
Because the anonymous function has no name, it cannot be referred to later. Rather, the anonymous function is typically created at the point it is passed to another function.
Or it’s possible to immediately invoke it (this is not a common usage):
;; operation (function) argument
;; -------------------------------- --------------
( (fn [message] (println message)) "Hello world!" )
;; Hello world!
Here we defined the anonymous function in the function position of a larger expression that immediately invokes the expression with the argument.
Many languages have both statements, which imperatively do something and do not return a value, and expressions which do. Clojure has only expressions that return a value. We’ll see later that this includes even flow control expressions like if
.
defn
vs fn
It might be useful to think of defn
as a contraction of def
and fn
. The fn
defines the function and the def
binds it to a name. These are equivalent:
(defn greet [name] (str "Hello, " name))
(def greet (fn [name] (str "Hello, " name)))
There is a shorter form for the fn
anonymous function syntax implemented in the Clojure reader: #()
. This syntax omits the parameter list and names parameters based on their position.
%
is used for a single parameter
%1
, %2
, %3
, etc are used for multiple parameters
%&
is used for any remaining (variadic) parameters
Nested anonymous functions would create an ambiguity as the parameters are not named, so nesting is not allowed.
;; Equivalent to: (fn [x] (+ 6 x))
#(+ 6 %)
;; Equivalent to: (fn [x y] (+ x y))
#(+ %1 %2)
;; Equivalent to: (fn [x y & zs] (println x y zs))
#(println %1 %2 %&)
One common need is an anonymous function that takes an element and wraps it in a vector. You might try writing that as:
;; DO NOT DO THIS
#([%])
This anonymous function expands to the equivalent:
(fn [x] ([x]))
This form will wrap in a vector and try to invoke the vector with no arguments (the extra pair of parentheses). Instead:
;; Instead do this:
#(vector %)
;; or this:
(fn [x] [x])
;; or most simply just the vector function itself:
vector
apply
The apply
function invokes a function with 0 or more fixed arguments, and draws the rest of the needed arguments from a final sequence. The final argument must be a sequence.
(apply f '(1 2 3 4)) ;; same as (f 1 2 3 4)
(apply f 1 '(2 3 4)) ;; same as (f 1 2 3 4)
(apply f 1 2 '(3 4)) ;; same as (f 1 2 3 4)
(apply f 1 2 3 '(4)) ;; same as (f 1 2 3 4)
All 4 of these calls are equivalent to (f 1 2 3 4)
. apply
is useful when arguments are handed to you as a sequence but you must invoke the function with the values in the sequence.
For example, you can use apply
to avoid writing this:
(defn plot [shape coords] ;; coords is [x y]
(plotxy shape (first coords) (second coords)))
Instead you can simply write:
(defn plot [shape coords]
(apply plotxy shape coords))
let
let
binds symbols to values in a "lexical scope". A lexical scope creates a new context for names, nested inside the surrounding context. Names defined in a let
take precedence over the names in the outer context.
;; bindings name is defined here
;; ------------ ----------------------
(let [name value] (code that uses name))
Each let
can define 0 or more bindings and can have 0 or more expressions in the body.
(let [x 1
y 2]
(+ x y))
This let
expression creates two local bindings for x
and y
. The expression (+ x y)
is in the lexical scope of the let
and resolves x to 1 and y to 2. Outside the let
expression, x and y will have no continued meaning, unless they were already bound to a value.
(defn messenger [msg]
(let [a 7
b 5
c (clojure.string/capitalize msg)]
(println a b c)
) ;; end of let scope
) ;; end of function
The messenger function takes a msg
argument. Here the defn
is also creating lexical scope for msg
- it only has meaning within the messenger
function.
Within that function scope, the let
creates a new scope to define a
, b
, and c
. If we tried to use a
after the let expression, the compiler would report an error.
The fn
special form creates a "closure". It "closes over" the surrounding lexical scope (like msg
, a
, b
, or c
above) and captures their values beyond the lexical scope.
(defn messenger-builder [greeting]
(fn [who] (println greeting who))) ; closes over greeting
;; greeting provided here, then goes out of scope
(def hello-er (messenger-builder "Hello"))
;; greeting value still available because hello-er is a closure
(hello-er "world!")
;; Hello world!
Below is a summary of calling conventions for calling into Java from Clojure:
Task | Java | Clojure | |
---|---|---|---|
Instantiation |
|
|
|
Instance method |
|
|
|
Instance field |
|
|
|
Static method |
|
|
|
Static field |
|
|
1) Define a function greet
that takes no arguments and prints "Hello". Replace the ___
with the implementation:
(defn greet [] _)
2) Redefine greet
using def
, first with the fn
special form and then with the #()
reader macro.
;; using fn
(def greet __)
;; using #()
(def greet __)
3) Define a function greeting
which:
Given no arguments, returns "Hello, World!"
Given one argument x, returns "Hello, x!"
Given two arguments x and y, returns "x, y!"
;; Hint use the str function to concatenate strings
(doc str)
(defn greeting ___)
;; For testing
(assert (= "Hello, World!" (greeting)))
(assert (= "Hello, Clojure!" (greeting "Clojure")))
(assert (= "Good morning, Clojure!" (greeting "Good morning" "Clojure")))
4) Define a function do-nothing
which takes a single argument x
and returns it, unchanged.
(defn do-nothing [x] ___)
In Clojure, this is the identity
function. By itself, identity is not very useful, but it is sometimes necessary when working with higher-order functions.
(source identity)
5) Define a function always-thing
which takes any number of arguments, ignores all of them, and returns the number 100
.
(defn always-thing [__] ___)
6) Define a function make-thingy
which takes a single argument x
. It should return another function, which takes any number of arguments and always returns x.
(defn make-thingy [x] ___)
;; Tests
(let [n (rand-int Integer/MAX_VALUE)
f (make-thingy n)]
(assert (= n (f)))
(assert (= n (f 123)))
(assert (= n (apply f 123 (range)))))
In Clojure, this is the constantly
function.
(source constantly)
7) Define a function triplicate
which takes another function and calls it three times, without any arguments.
(defn triplicate [f] ___)
8) Define a function opposite
which takes a single argument f
. It should return another function which takes any number of arguments, applies f
on them, and then calls not
on the result. The not
function in Clojure does logical negation.
(defn opposite [f]
(fn [& args] ___))
In Clojure, this is the complement function.
(defn complement
"Takes a fn f and returns a fn that takes the same arguments as f,
has the same effects, if any, and returns the opposite truth value."
[f]
(fn
([] (not (f)))
([x] (not (f x)))
([x y] (not (f x y)))
([x y & zs] (not (apply f x y zs)))))
9) Define a function triplicate2
which takes another function and any number of arguments, then calls that function three times on those arguments. Re-use the function you defined in the earlier triplicate exercise.
(defn triplicate2 [f & args]
(triplicate ___))
10) Using the java.lang.Math class (Math/pow
, Math/cos
, Math/sin
, Math/PI
), demonstrate the following mathematical facts:
The cosine of pi is -1
For some x, sin(x)^2 + cos(x)^2 = 1
11) Define a function that takes an HTTP URL as a string, fetches that URL from the web, and returns the content as a string.
Hint: Using the java.net.URL class and its openStream
method. Then use the Clojure slurp
function to get the content as a string.
(defn http-get [url]
___)
(assert (.contains (http-get "https://www.w3.org") "html"))
In fact, the Clojure slurp
function interprets its argument as a URL first before trying it as a file name. Write a simplified http-get:
(defn http-get [url]
___)
12) Define a function one-less-arg
that takes two arguments:
f
, a function
x
, a value
and returns another function which calls f
on x
plus any additional arguments.
(defn one-less-arg [f x]
(fn [& args] ___))
In Clojure, the partial
function is a more general version of this.
13) Define a function two-fns
which takes two functions as arguments, f
and g
. It returns another function which takes one argument, calls g
on it, then calls f
on the result, and returns that.
That is, your function returns the composition of f
and g
.
(defn two-fns [f g]
___)