If#
(if test then else?)
A control flow structure. First evaluates test. If test evaluates to true
, only the then form is evaluated and the result is returned. If test evaluates to false
only the else form is evaluated and the result is returned. If no else form is given, nil
will be returned.
The test evaluates to false
if its value is false
or equal to nil
. Every other value evaluates to true
. In sense of PHP this means (test != null && test !== false
).
(if true 10) # Evaluates to 10
(if false 10) # Evaluates to nil
(if true (print 1) (print 2)) # Prints 1 but not 2
(if 0 (print 1) (print 2)) # Prints 2
(if nil (print 1) (print 2)) # Prints 2
(if [] (print 1) (print 2)) # Prints 2
Case#
(case test & pairs)
Evaluates the test expression. Then iterates over each pair. If the result of the test expression matches the first value of the pair, the second expression of the pair is evaluated and returned. If no match is found, returns nil.
(case (+ 7 5)
3 :small
12 :big) # Evaluates to :big
(case (+ 7 5)
3 :small
15 :big) # Evaluates to nil
(case (+ 7 5)) # Evalutes to nil
Cond#
(cond & pairs)
Iterates over each pair. If the first expression of the pair evaluates to logical true, the second expression of the pair is evaluated and returned. If no match is found, returns nil.
(cond
(neg? 5) :negative
(pos? 5) :positive) # Evaluates to :positive
(cond
(neg? 5) :negative
(neg? 3) :negative) # Evaluates to nil
(cond) # Evaluates to nil
Loop#
(loop [bindings*] expr*)
Creates a new lexical context with variables defined in bindings and defines a recursion point at the top of the loop.
(recur expr*)
Evaluates the expressions in order and rebinds them to the recursion point. A recursion point can be either a fn
or a loop
. The recur expressions must match the arity of the recursion point exactly.
Internally recur
is implemented as a PHP while loop and therefore prevents the Maximum function nesting level errors.
(loop [sum 0
cnt 10]
(if (= cnt 0)
sum
(recur (+ cnt sum) (dec cnt))))
(fn [sum cnt]
(if (= cnt 0)
sum
(recur (+ cnt sum) (dec cnt))))
Foreach#
(foreach [value valueExpr] expr*)
(foreach [key value valueExpr] expr*)
The foreach
special form can be used to iterate over all kind of PHP datastructures for side-effects. The return value of foreach
is always nil
. The loop
special form should be preferred of the foreach
special form whenever possible.
(foreach [v [1 2 3]]
(print v)) # Prints 1, 2 and 3
(foreach [k v {"a" 1 "b" 2}]
(print k)
(print v)) # Prints "a", 1, "b" and 2
For#
A more powerful loop functionality is provided by the for
loop. The for
loop is an elegant way to define and create arrays based on existing collections. It combines the functionality of foreach
, let
, if
and reduce
in one call.
(for head body+)
The head
of the loop is a vector that contains a
sequence of bindings and modifiers. A binding is a sequence of three
values binding :verb expr
. Where binding
is a binding as
in let
and :verb
is one of the following keywords:
:range
loop over a range, by using the range function.:in
loops over all values of a collection.:keys
loops over all keys/indexes of a collection.:pairs
loops over all key value pairs of a collection.
After each loop binding additional modifiers can be applied. Modifiers
have the form :modifier argument
. The following modifiers are supported:
:while
breaks the loop if the expression is falsy.:let
defines additional bindings.:when
only evaluates the loop body if the condition is true.:reduce [accumulator initial-value]
Instead of returning a list, it reduces the values intoaccumulator
. Initiallyaccumulator
is bound toinitial-value
.
(for [x :range [0 3]] x) # Evaluates to [0 1 2]
(for [x :range [3 0 -1]] x) # Evaluates to [3 2 1]
(for [x :in [1 2 3]] (inc x)) # Evaluates to [2 3 4]
(for [x :in {:a 1 :b 2 :c 3}] x) # Evaluates to [1 2 3]
(for [x :keys [1 2 3]] x) # Evaluates to [0 1 2]
(for [x :keys {:a 1 :b 2 :c 3}] x) # Evaluates to [:a :b :c]
(for [[k v] :pairs {:a 1 :b 2 :c 3}] [v k]) # Evaluates to [[1 :a] [2 :b] [3 :c]]
(for [[k v] :pairs [1 2 3]] [k v]) # Evaluates to [[0 1] [1 2] [2 3]]
(for [[k v] :pairs {:a 1 :b 2 :c 3} :reduce [m {}]]
(put m k (inc v))) # Evaluates to {:a 2 :b 3 :c 4}
(for [x :in [2 2 2 3 3 4 5 6 6] :while (even? x)] x) # Evaluates to [2 2 2]
(for [x :in [2 2 2 3 3 4 5 6 6] :when (even? x)] x) # Evaluates to [2 2 2 4 6 6]
(for [x :in [1 2 3] :let [y (inc x)]] [x y]) # Evaluates to [[1 2] [2 3] [3 4]]
(for [x :range [0 4] y :range [0 x]] [x y]) # Evaluates to [[1 0] [2 0] [2 1] [3 0] [3 1] [3 2]]
Do#
(do expr*)
Evaluates the expressions in order and returns the value of the last expression. If no expression is given, nil
is returned.
(do 1 2 3 4) # Evaluates to 4
(do (print 1) (print 2) (print 3)) # Print 1, 2, and 3
Dofor#
(dofor [x :in [1 2 3]] (print x)) # Prints 1, 2, 3 and returns nil
(dofor [x :in [2 3 4 5] :when (even? x)] (print x)) # Prints 1, 2 and returns nil
Iterating over collections for side-effects is also possible with dofor
which has similar behavior to for
otherwise but returns nil
as foreach
does.
Exceptions#
(throw expr)
The expr is evaluated and thrown, therefore expr must return a value that implements PHP's Throwable
interface.
Try, Catch and Finally#
(try expr* catch-clause* finally-clause?)
All expressions are evaluated and if no exception is thrown the value of the last expression is returned. If an exception occurs and a matching catch-clause is provided, its expression is evaluated and the value is returned. If no matching catch-clause can be found the exception is propagated out of the function. Before returning normally or abnormally the optionally finally-clause is evaluated.
(try) # Evaluates to nil
(try
(throw (php/new \Exception))
(catch \Exception e "error")) # Evaluates to "error"
(try
(+ 1 1)
(finally (print "test"))) # Evaluates to 2 and prints "test"
(try
(throw (php/new \Exception))
(catch \Exception e "error")
(finally (print "test"))) # Evaluates to "error" and prints "test"