We stumbled upon an issue in our code today, and couldn't answer this Clojure question:
Does Clojure evaluate impure code (or calls to Java code) strictly or lazily?
It seems that side-effects + lazy sequences can lead to strange behavior.
Here's what we know that led to the question:
Clojure has lazy sequences:
user=> (take 5 (range)) ; (range) returns an infinite list
(0 1 2 3 4)
And Clojure has side-effects and impure functions:
user=> (def value (println 5))
5 ; 5 is printed out to screen
user=> value
nil ; 'value' is assigned nil
Also, Clojure can make calls to Java objects, which may include side-effects. However, side-effects may interact poorly with lazy evaluation:
user=> (def my-seq (map #(do (println %) %) (range)))
#'user/my-seq
user=> (take 5 my-seq)
(0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
0 1 2 3 4)
So it returned the first 5 elements, but printed the first 31!
I assume the same kinds of problems could occur if calling side-effecting methods on Java objects. This could make it really hard to reason about code and figure out what's going to happen.
Ancillary questions:
- Is it up to the programmer to watch out for and prevent such situations? (Yes?)
- Besides sequences, does Clojure perform strict evaluation? (Yes?)