Drawing a winner in Clojure again – this time with function composition and recur

Functional programming is for me about function composition and no mutable state in-between if possible. I knew I could do better than Drawing a winner in Clojure (in an almost purely functional approach) and the comments showed how far I was from “purely functional approach” (I should’ve known it’s not a good idea to use it for the title).

I spent the whole day tinkering the application in which the names of participants are read from stdin and no state is used (I suppose loop-recur doesn’t count). How do you like it now? Is it more functional than the previous version? How much imperative thinking is in the code? Would you, functional purists, accept the version?

You couldn’t believe how happy I am with the version :)

(defn read-names
  "Collect names from stdin. Enter alone finishes reading. Returns a list of names."
  []
  (loop [names '()]
    (let [_ (print "Enter the name of the attendee (press ENTER to finish): ")
          _ (flush)
          user (read-line)]
      (if (seq (.trim user)) 
        (recur (conj names user))
        names))))

(defn draw-winner
  "Draw a winner"
  [names]
  (if (seq names)
    (rand-nth names)))

(draw-winner (read-names))

BTW, For the initial version of the draw-winner function I used (if (not (empty? names)) ...), but felt uncomfortable with it. I ran across (if-not (empty? coll), but still wasn’t happy with it. When I read the doc for empty? it said “Please use the idiom (seq x) rather than (not (empty? x))”. And so I did. With the doc saying “use the idiom” I couldn’t have been happier! :)

Be Sociable, Share!
This entry was posted in Languages.

6 Responses to Drawing a winner in Clojure again – this time with function composition and recur

  1. Reads nicer now. I can understand code at least :P Thanks for sharing :)

    • I believe that the more functional code the more readable it is. Glad you liked it. Inspiration is yours, pleasure’s mine :)

    • Daniel Janus says:

      I’d rewrite

      read-names

      as follows (apologies for broken indentation, WordPress seems to inevitably lose the formatting):

      (defn read-line-with-prompt [prompt]
        (print prompt)
        (flush)
        (read-line))

      (defn read-names []
        (take-while seq (repeatedly (partial read-line-with-prompt "Enter the name of the attendee (press ENTER to finish): "))))

      Rule of thumb: there’s very rarely a need to use

      loop/recur

      directly (and such need mostly stems from performance requirements).

  2. Craig says:

    I like the take-while version better, but I just wanted to point out that ‘() is non-idiomatic. For starters, in Clojure there is no need to quote the empty list: () will serve just as well, since Clojure doesn’t treat the empty list as nil, and it’s not an evaluation error. But if it were me, I would probably use (list).

  3. You couldn’t imagine how helpful the comment was. I’m just about to present Clojure at 33rd degree conference and am going to use it!

Leave a Reply

%d bloggers like this: