2009-01-18

Setup mods (clojure)

After much tearing of hair, many emacs restarts, many killings of *inferior-lisp*, I finally decided to give up on the pure-swank way of calling java. Up until yesterday I just had this:

  
(setq swank-clojure-jar-path "/home/joseph/src/clojure/clojure.jar")
(setq swank-clojure-extra-classpaths 
      (list "/home/joseph/clojure/clojure-contrib.jar" 
            "/home/joseph/lib/commons-httpclient-3.1.jar"))
  

Last night I reorganized everything basically following Bill Clementson's setup. Now all the classpath business goes into an environment variable and gets called by an almost empty shell script. For me, the main advantage of this setup is that it was easier to see how java was being called, so that it was easier to debug the rest of my setup.

To make a long story short, I was finally able to import org.apache.commons.httpclient this morning.

2009-01-16

recur is a trip

This is really just an update to my previous post, but I just added recur to my recursive function.

The Clojure documentation isn't that verbose, in general, and it took my n00b brain some time, and some luck, and some nasty messages from the compiler, to figure out how you actually use recur. Basically, it looks like that, just as you are about to have your function call itself, instead of using the name of your function, you call recur instead. Wow. Why does that seem cool?

Baby clojure step number two: my very first mind expansion

So, today, after just a little bit more fiddling getting Clojure/swank/slime working on my desktop box, I was able to actually run into a more Clojurish obstacle. But first I had better explain my little project that I just mentioned yesterday.

I used to read RSS feeds with Gnus, but ever since I switched to using the Gmail IMAP interface with Gnus (rather than fetching mail to my local server and reading it from there), the extra time spent reloading all my feeds every time I wanted to check my mail started to discourage me. And that was the end of nnrss for me. At least for awhile.

The other day I started to think that nnrss might work for me again if I kept local copies of the feeds and used wget or something to keep them up to date. As I started putting a bash script together, I started to realize that my specs were quickly surpassing my bash skills: keeping track of the last time fetched (because wget won't do it if you change the name of the file you download, which you have to do because feeds often have the same filename...), conditionally running Atom feeds through an XSL stylesheet to make them readable for nnrss, and probably more as the list got longer. And I did look around for some kind of ready-made solution to all of this but everything I found was somehow too complete. So just as I was about to start writing some Perl, I thought: "you're in no hurry, do this in Clojure and learn a thing or two". So here I am.

Anyway, step one right now is to read a text file with a list of URLs and a feedname. Next I will add a "type" field or maybe an "atomp" field to pick out those that need furthing parsing. So then we end up looking at ideal material for a Perl one-liner but a metaphysical challenge for the Clojure 00ber-n00b that I am. Yesterday I mentioned my first flailings that got me as far as reading a file, splitting the lines and printing them back out. Heady stuff, I know.

Today's challenge was stuffing everything back into what was to become my feed "database". Using Stuart Halloway's rewrite of Peter Seibel's PCL, specifically the now-famous CD database, I had settled on using a set of structs to contain all my information.

Everything seemed very calm and smooth for awhile. I could write functions for making structs, adding them to the set (#{}). I could read my data file, I was using first and second and felt like I was back in Lisp.

And then suddenly I hit the wall of immutability.

Here is what I wanted to do:

  
(defn parse-rsslist-file [db feedlist-filename]
 (with-open [r (reader feedlist-filename)]
   (doseq [line (line-seq r)]
     (let [url (first (.split line " "))
           nam (second (.split line " "))]
       (add-feeds db (struct feed nam nil url nil)))))
       db)
  

I was stupidly trying to loop through the lines and accumulate the results in db. That is what I would have done in Common Lisp and just about anything else. When my function kept just returning an empty #{}, I finally realized that I was face to face with real functional programming and one of the aspects of Clojure that has been commented the most.

This led to a new exploration of the Clojure docs and a tentative understanding of the difference between a sequence and a collection and a list. I knew that I needed to write a recursive function but I was not sure exactly what (line-seq r) was spitting out. Happily for me, it turns out that everything does end up working more or less like a list, and so with very little fiddling I was able to get my magnificent file reading function to work.

Behold!

    
(defn parse-rsslist-file [db urlfile]
  (with-open [r (reader urlfile)]
    (parssrss db (line-seq r))))

(defn parssrss [db sq]
  (if (not sq)
    db
    (parssrss (db-add-line db (first sq)) (rest sq))))

(defn db-add-line [db line]
  (let [lspl (.split line " ")]
    (cons (struct-map feed 
                  :name (second lspl)
                  :title nil
                  :url (first lspl)) db)))
    
  

2009-01-15

Tiny baby clojure steps

Yesterday I started actually trying to do something with Clojure. Here are my very, very first impressions of what it is like to actually get your feet wet, or your fingers dirty or whatever. This can probably all be resumed with: everything is much more difficult if you don't have any experience with Java. Like me.

This is of course assuming that your system is working correctly. I only finally ended up getting slime, swank and clojure all working together correctly. It isn't that hard, or at least it doesn't seem like it once you see how things fit together. Just a couple of variables for swank, your .jars in a good spot and you are ready to go. It took me a while to realize that of course. And I made stupid mistakes like not compiling clojure-contrib yet expecting it to work. Stupid stupid mistakes.

My initial task was to try to read a file line-by-line. A very Perl-ish thing to do I guess, but it seemed to be a reasonable thing to do. Also, in the initial version of my little project (more about that later), everything started with a config file full of URLs that I was trying to read with bash.

So how do you read a file line by line in Clojure?

Well, here is what I have right now. It works.

(use '[clojure.contrib.duck-streams :only (reader)]) (with-open [r (reader "/home/joseph/localrss/rss_urls")] (doseq [line (line-seq r)] (println line)))

What is strange is that Java is always close by. Just for reading a file, you need Java objects. Here "duck-streams" are a wrapper around some Java stream things. And I had to bring the clojure-contrib .jar into my configuration for this to work. This is just something to get used to, I suppose, and probably feels perfectly natural for Java programmers.

The same seems to go for string functions. The official docs just say :

Clojure strings are Java Strings. See also Printing. user=> (map (fn [x] (.toUpperCase x)) (.split "Dasher Dancer Prancer" " ")) ("DASHER" "DANCER" "PRANCER")
Those
.toUpperCase
and
.split
s look suspiciously like macros calling Java functions, so it looks like I'll be getting chummy with those soon too.

Interestingly, I haven't seen anything about strings being vectors or sequences of some kind, as in Common Lisp. I guess all of that gets thrown away once you beat into your n00b brain that "Clojure strings are Java strings".