Thursday, December 30, 2010

Laziness is hard work

UPDATE 2:
I was re-reading a bit of Stuart Halloway's book "Programming Clojure", and he states that the repl is NOT lazy.  If for example you create a lazy sequence, you should store it in a var if you are at the repl, otherwise the repl will eagerly attempt to evalute everything.  This is why the code WITHOUT being wrapped by dorun works in the repl, but not as a standalone program.


UPDATE:
Interstingly, while the code snippets below work at the repl as-is, if you run them as a program they will not.  What is most interesting is that I didn't think the code below would work, which is why I was mildly surprised it did.  The title of the post came from the fact that I thought making calls in these lazy sequences wouldn't work.

If you do the code below, then you will have to wrap your code in  dorun macro.  This forces the evaluation of functions with side effects (which these do), and thus will allow it to work.

I tried a few things in my clojure code, thinking that it would make my code cleaner.  Although the doto macro is nice, it still is a lot of boilerplate...

(doto button-group
   (.add (JRadioButton. "One"))
   (.add (JRadioButton. "Two"))
   (.add (JRadioButton. "Three")))

ooooh, I got to leave out button-group in all the function calls....that's still not exactly easy on the fingers.  But you'll see code like that in a lot of other tutorials or sites.  So I thought, why not call map on a collection instead?


(def rdo-btns
  (map #(.add button-group %1) [ (JRadioButton. "One")
                                                    (JRadioButton. "Two")
                                                    (JRadioButton. "Three") ]))

This will work, but remember that it only works because of side-effects.  The call .add is doing something to button-group and is in effect changing the state of button-group.  In a pure function, this wouldn't be the case. One might argue that this is actually slightly longer, but it has the advantage that rdo-btns now contains a sequence of the JRadioButtons, while the first example above would require extracting the JRadioButton from button-group.

Another possibility is to use a for sequence comprehension like so:

(def rdo-btns
  (for [ rdo [ (JRadioButton. "One") (JRadioButton. "Two") (JRadioButton. "Three") ] ] (.add jpanel rdo)))

This will also return a sequence like map will, and again, it only works because of side effects.  Nevertheless, since we're working with java classes, that's bound to happen.  So it might be better to put asterisks around the symbols like *jpanel* or *button-group* to indicate a symbol with mutating state.

No comments:

Post a Comment