Drawing a winner in Clojure (in an almost purely functional approach)

There is a need in Warszawa Java User Group to have a way to draw a winner of a free license for a product from JetBrains amongst the attendees of a meeting. We couldn’t figure out whether the draw should be at the beginning of a meeting or its end. Some argued it doesn’t really matter, and with all the options on the table it was a tough exercise to pick the right one (or one that’s the closest).

When I proposed to define a role to manage draws and accept that another person from meeting attendees can run a draw, Krzysiek Nielepkowicz pointed out that it reminded him about closures in Java that close over a free variable that’s a person to run a draw.

It caught my attention and was a kind of call for action to write a Clojure script for the scenario. It was as follows (I was writing it off the top of my head):

(defn drawer []
  (fn [attendee]
    (draw attendee)))

(def lukasz-drawer (drawer))

(declare select-drawer-from-attendees)

(lukasz-drawer (select-drawer-from-attendees))

It was all about defining a higher-order function – drawer – to return a function of one argument. I wasn’t happy with the first draft of the Clojure script, though.

I spent the other day on another, hopefully better, solution. I’ve been reading a few articles about the let-over-lambda concept and quite recently about state management in Clojure in Practical Clojure from Apress so the exercise seemed a good fit to try out what I’ve learnt so far. The aim was to create a Clojure script as much functional as possible.

Let’s start from the end – with the final call of a function to draw a winner. I believe that’s how applications should be/are written (with a client API in mind). I also believe this approach begs for a screencast and more TDD-like approach (with Clojure’s midje, I guess). I’m working on it. Before it’s available, below you can find a textual version of the script.

(draw-winner (participants)) ;; returns a winner of a draw
                             ;; it must be a function of a single argument - participants

;; ps stands for participants
(defn draw-winner [ps]
  (let [idx (rand-int (count ps))]
    (nth ps idx)))

;; participants is a function that returns registered participants
;; that are stored in a vector
;; or registers one
(defn handle-participants []
  (let [users (atom [])]
    (fn [& args]
      (let [u (first args)]
        (if (not (nil? u))
          (swap! users conj u)
          @users)))))

(def register (handle-participants))

;; register participants - a meeting attendees
;; register as a verb
(register :jacek)
(register :agatka)
(register :iwetka)
(register :patryk)
(register :maksym)

;; check who's registered
;; register as a noun
(register)

;; "implement" the former participants
(def participants register)

;; draw a winner
(draw-winner (participants))

How does it look like? What would you change to improve its functional look? Shouldn’t register return participants with no need to keep them internally and no return value? Let me know how I could improve the functional programming style of mine I’m trying to get used to.

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

11 Responses to Drawing a winner in Clojure (in an almost purely functional approach)

  1. Why is handle-participants so compilcated ? I understand its only an exercise. In Scala I would do it in three lines :)
    val participants = Array(“jacek”, “agatka”, …)
    val rand = Random.nextInt(participants.length)
    val winner = participants(rand)

  2. The idea is to have functions only. I strove for a solution that others would call purely functional which I believe is far from what I proposed.

    In your solution there’re 3 vals that I wished to disregard completely – I’ve got two and guess could squash them all.

    register-participants does two things – register a user (in your case it’d be to add an item to the array) and list registered users. It more or less conforms to the Array class you used in your example, but since I’m aiming at functional solution I need functions only.

    I might’ve overcomplicated it slightly, too.

    As an exercise for you – could you build an application in Scala with functions only? No vals. Just functions and be able to register users dynamically so I can read them in from System.in? Strive for pure functions only. I don’t think your app is functional yet.

  3. Well definition of purely functional means for same arguments always return same results so your is not also purely functional in strict sense :) I can remove what so ever, only non functional issue here is Array which is mutable I can replace that for Vector and will be both fast and immutable :)

    • Almost. For me, purely functional was a term to express a need to base my processing on functions only (which could have some side-effects), but you’re right I might’ve been overused it.

      I’ve been thinking about your solution and what I was up to, and came up with a challenge – would you write your app with immutable data structures and collect users as they enter a room with your app open? I.e. imagine your app is collecting names and when all the names are given, draw a winner. I may have a proposal in Clojure tonight.

  4. Craig Andera says:

    One thing I would change would be to get rid of the if statement in handle-participants. You can do that by making the fn multi-arity, and have the zero-arity overload return the value of the atom.

    (defn handle-participants []
      (let [users (atom [])]
        (fn
          ([] @users)
          ([& args] (swap! users conj (first args)))))

    Although obviously at this point it’s a bit weird to use a variable-arity overload, since you’re not doing anything with it. So maybe this instead:

    (defn handle-participants []
      (let [users (atom [])]
        (fn
          ([] @users)
          ([u] (swap! users conj u))))

    or even

    (defn handle-participants []
      (let [users (atom [])]
        (fn
          ([] @users)
          ([& users] (swap! users concat users))))
  5. Pingback: Drawing a winner in Clojure again – this time with function composition and recur | Japila :: verba docent, exempla trahunt

Leave a Reply

%d bloggers like this: