Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure output binding reset in for loop

I'm new to Clojure, and am trying to redirect output to a file by rebinding *out*. In a simple case, it works nicely:

(binding [*out* (new java.io.FileWriter "test.txt")]
  (println "Hi"))

This does what I expect, printing "Hi" to the file test.txt. However, if I introduce a for loop, things go awry:

(binding [*out* (new java.io.FileWriter "test.txt")]
  (for [x [1 2]]
    (println "Hi" x)))

This time, all the output goes to stdout, and the file is empty. What's going on here?

I'm using Leiningen, if that makes any difference:

Leiningen 2.0.0 on Java 1.7.0_13 Java HotSpot(TM) 64-Bit Server VM
like image 496
shawkinaw Avatar asked Dec 03 '25 23:12

shawkinaw


1 Answers

you have been bitten by the lazy bug.

put a doall or dorun around the for and within the binding

(binding [*out* (new java.io.FileWriter "test.txt")]
  (doall (for [x [1 2]]
           (println "Hi" x))))

In your example the printing is happening then the result is printed by the repl after it is returned from the binding. So at the time of printing the binding is no longer in place.

Nothing is printed because result is a lazy sequence that will later be evaluated when used

user> (def result (binding [*out* (new java.io.FileWriter "test.txt")]
        (for [x [1 2]] 
          (println "Hi" x))))
#'user/result

When the repl prints the resuls the printlns are evaluated:

user> result
(Hi 1
Hi 2 
nil nil) 

If we force the evaluation of the lazy sequence returned by for within the binding nothing is printed to the repl,

user> (def result (binding [*out* (new java.io.FileWriter "test.txt")]
  (doall (for [x [1 2]]             
          (println "Hi" x))))) 
#'user/result 
user> result
(nil nil) 

and instead the output ends up in the file:

arthur@a:~/hello$ cat test.txt 
Hi 1
Hi 2
like image 183
Arthur Ulfeldt Avatar answered Dec 06 '25 14:12

Arthur Ulfeldt