How do I interpret merge-with that takes a fn in clojure?

Viewed 89

I'm currently learning Clojure and I'm a total beginner and I would appreciate some help understanding. I went through some code today and found this.

(let [timepoints (merge-with (fn [mf swt] [mf swt]) timepoint-max timepoint-sum )])

where mf, swt, timepoint-max and timepoint-sum looks something like

{"Timepoint1": 3, "Timepoint2": 2}

So what does the code above do?

I understand that we set the variable timepoints to be some sort of union between the two maps(?). But I'm especially confused about the fn [mf swt] [mf swt] part.

1 Answers

The expression (fn [mf swt] [mf swt]) is an anonymous clojure function that constructs a vector from the two arguments passed into it, e.g

((fn [mf swt] [mf swt]) :a :b)
;; => [:a :b]

The expression (merge-with (fn [mf swt] [mf swt]) timepoint-max timepoint-sum ) is a call to the function merge-with with first argument (fn [mf swt] [mf swt]), second argument timepoint-max and third argument timepoint-sum. Here is an example where we bind timepoint-max and timepoint-sum to some example values:

(def timepoint-max {:x 0 :y 1})
(def timepoint-sum {:y 100 :z 200})

(merge-with (fn [mf swt] [mf swt]) timepoint-max timepoint-sum)
;; => {:x 0, :y [1 100], :z 200}

Read the docs of merge-with to understand what it does:

Returns a map that consists of the rest of the maps conj-ed onto the first. If a key occurs in more than one map, the mapping(s) from the latter (left-to-right) will be combined with the mapping in the result by calling (f val-in-result val-in-latter).

In the above example, what we actually compute is the same as

{:x (:x timepoint-max)
 :y ((fn [mf swt] [mf swt]) (:y timepoint-max) (:y timepoint-sum))
 :z (:z timepoint-sum)}
;; => {:x 0, :y [1 100], :z 200}

where the function (fn [mf swt] [mf swt]) is used to combine the two values at the only overlapping key :y.

The full expression (let [timepoints (merge-with (fn [mf swt] [mf swt]) timepoint-max timepoint-sum )]) is a let form that binds values to symbols but it is not very useful because its *exprs part is empty, so it always evaluates to nil:

(let [timepoints (merge-with (fn [mf swt] [mf swt]) timepoint-max timepoint-sum )])
;; => nil

In order for it to evaluate to something else, e.g. timepoints, it would have to be modified to something like

(let [timepoints (merge-with (fn [mf swt] [mf swt]) timepoint-max timepoint-sum) ] 
  timepoints)
;; => {:x 0, :y [1 100], :z 200}
Related