Matthew Phillips makes the case of Clojure’s “for” comprehensions

(written by lawrence krubner, however indented passages are often quotes). You can contact lawrence at: lawrence@krubner.com, or follow me on Twitter.

I am lazy so I would probably handle this with “reduce” as I then have a free-form function in which I can do whatever I want. And yet, list comprehensions are more idiomatic. Their limits make it more obvious what the structure of data should be. And for deeply nested items, they can handle everything at once, rather than needing to do nested calls to “reduce”.

But you can also do it this way, using a list-comprehension in Clojure’s for syntax:

(for [site sites :when (= (:time-zone site) "Adelaide/Australia")
      station (:stations site)]
      (:id station))

This reads as: “select every site in sites, remove the ones that aren’t in the Adelaide time zone, select the stations from those, and return their ID”. This generates the same results as the first expressions, but reads pretty much as you’d say it to someone (and there’s no mapcat to forget).

…But it’s when you get to following example, a real part of a system I’ve been working on, that list-comprehension approach comes into its own:

(for [scheduled-task scheduled-tasks
      schedule (:schedules scheduled-task)
      [station-id span] (get spans (:id schedule))
      :when (in-span? span current-time)]
  (let [local-time (-> current-time
                       (t/to-time-zone
                         (timezone-for-station-id sites station-id)))]
    [station-id (make-station-task scheduled-task schedule local-time)]))

Without going into details, this has three levels of nesting and a filter, generating a series of pairs of station ID’s and tasks to be scheduled for them. For someone familiar with the system, this will be far easier to comprehend than a map/filter.

Post external references

  1. 1
    http://sapient-pair.com/blog/2015/09/03/for-clarity/
Source