Getting some baby steps going with Clojure. What threw me for a while is how difficult it was to get to do some basic output.
I wanted to see the JVM system properties. It's easy to get them, Java inter-operation is strong in Clojure, but in a readable format?
user=> (System/getProperties) #=(java.util.Properties. {"java.runtime.name" "Java(TM) 2 Runtime Environment, Standard Edition", "sun.boot.library.path" "/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Libraries", "java.vm.version" "1.5.0_16-133", "awt.nativeDoubleBuffering" "true", "gopherProxySet" "false", "java.vm.vendor" "Apple Inc.", "java.vendor.url" "http://www.apple.com/", "path.separator" ":", "java.vm.name" "Java HotSpot(TM) Client VM", "file.encoding.pkg" "sun.io", "sun.java.launcher" "SUN_STANDARD", "user.country" "US", "sun.os.patch.level" "unknown", "java.vm.specification.name" "Java Virtual Machine Specification", "user.dir" "/Users/Howard", "java.runtime.version" "1.5.0_16-b06-284", "java.awt.graphicsenv" "apple.awt.CGraphicsEnvironment", "java.endorsed.dirs" "/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/endorsed", "os.arch" "i386", "java.io.tmpdir" "/tmp", "line.separator" "\n", "java.vm.specification.vendor" "Sun Microsystems Inc.", "os.name" "Mac OS X", "sun.jnu.encoding" "MacRoman", "java.library.path" ".:/Users/Howard/Library/Java/Extensions:/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java", "java.specification.name" "Java Platform API Specification", "java.class.version" "49.0", "sun.management.compiler" "HotSpot Client Compiler", "os.version" "10.5.5", "user.home" "/Users/Howard", "user.timezone" "", "java.awt.printerjob" "apple.awt.CPrinterJob", "file.encoding" "MacRoman", "java.specification.version" "1.5", "java.class.path" "/usr/local/clojure/clojure-contrib.jar:/usr/local/clojure/clojure-lang-1.0-SNAPSHOT.jar", "user.name" "Howard", "java.vm.specification.version" "1.0", "java.home" "/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home", "sun.arch.data.model" "32", "user.language" "en", "java.specification.vendor" "Sun Microsystems Inc.", "awt.toolkit" "apple.awt.CToolkit", "java.vm.info" "mixed mode", "java.version" "1.5.0_16", "java.ext.dirs" "/Users/Howard/Library/Java/Extensions:/Library/Java/Extensions:/System/Library/Java/Extensions:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/ext", "sun.boot.class.path" "/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/classes.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/ui.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/laf.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/sunrsasign.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/jsse.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/jce.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/charsets.jar", "java.vendor" "Apple Inc.", "file.separator" "/", "java.vendor.url.bug" "http://bugreport.apple.com/", "sun.io.unicode.encoding" "UnicodeLittle", "sun.cpu.endian" "little", "mrj.version" "1050.1.5.0_16-284", "sun.cpu.isalist" ""}) user=>
So I thought I'd print out the results, one item per line.
user=> (range 5) (0 1 2 3 4) user=> (for [x (range 5)] (println x)) (0 nil 1 nil 2 nil 3 nil 4 nil) user=>
Hm. This took a while to figure out. The trick is that for is a lazy operator, used for list comprehensions. That is, the sequence produced by for is lazy, you have to start navigating the sequence for it to fully execute. Thus, what we see above is two different streams of output, mixed together: A list: (nil nil nil nil nil)
interspersed with the println
calls. Here's the order of operations:
- The Repl starts to print the sequence, starting with a "(" before the first atom
- The first atom is evaluated, printing "0" and returning nil
- The Repl prints the "nil"
- The second atom is evaluated (lazily), printing "1" and returning nil
- The Repl prints the second "nil"
- And so on ...
That's a lesson ... in a lazily-evaluated world, even the most basics ideas have to be thrown out the window. You would never see this in Haskell, because it creates impenetrable barriers between functional, side-effect free code and any code that communicates with the outside world, even something as simple as println.
The solution? Collapse the list to a string and print that at the end:
user=> (use 'clojure.contrib.str-utils) nil user=> (println (str-join "\n" (range 5))) 0 1 2 3 4 nil user=>
Baby steps. Sometimes the simplest things are tricky, but I suspect the more advanced things are easier.
I've found an easier way to accomplish what I want:
ReplyDelete(dorun (map println (range 5)))
The (dorun) function evaluates a lazy seq for the purposes of side effects, then returns null.
Shawn (at Formos) points out that:
ReplyDelete(doseq n (range 5) (println n))
is more idiomatic, as it exists just to perform side effects.
The code in comment #2 doesn't work. You need
ReplyDeletedoseq [n (range 5)] (println n))
Your code doesn't work as well. You need
ReplyDelete(doseq [n (range 5)] (println n))