Pure Danger Tech


Learning Clojure #10: Duck streams

26 Feb 2010

In my last Clojure post, I was trying to demonstrate how to use the with-command-line library. In the process, I built a little line/word count tool as an example and that also led me to explore the awesome duck-streams library that’s available as well. There’s not actually too much code to show because the duck-streams lib makes things really simple.

Here’s a simple line count function:

(defn count-lines [file] 
	(count (read-lines file)))

The read-lines function takes something that can be coerced into a reader (a file name, a url, a Reader, an InputStream, a Socket, etc) and lazily returns lines read from the source. And then we just count them up.

Counting characters is also pretty easy and doesn’t even require duck-streams, just the slurp function in clojure.core:

(defn count-chars [file] 
	(count (slurp file)))

slurp just reads the whole file into a string and returns it. This won’t work for big files of course but was no big deal for this simple demo app. You can also use slurp* in duck-streams to slurp from a Reader instead of a file.

Those functions took literally 5 minutes to write so I figured I’d take on count-words as well and it’s almost as easy:

(defn count-words [file]
	(count (.split #"\s+" (slurp file))))

Here I also slurp the entire file first. I use the #"" syntax to define a regular expression (really a java.util.Pattern), here I’m using the built-in character set \s for whitespace and + to indicate one or more spaces.

I’m using .split which invokes the Java method but you can also use a slightly prettier and more capable function split in clojure.contrib.str-utils2.

That’s it! I’m really just scratching the surface of duck-streams though – it has tons of useful stuff in it for all manner of I/O.