Why Not a Function #12: deep-merge (with)

(defn deep-merge
  [m1 m2 & {:keys [with]}]
  (into {}
    (map (fn [k]
           (let [v1 (get m1 k)
                 v2 (get m2 k)]
             [k (cond
                  (and (map? v1) (map? v2)) (deep-merge v1 v2 :with with)
                  (and with (contains? m1 k) (contains? m2 k)) (with v1 v2)
                  (contains? m2 k) v2
                  :else v1)]))
      (set/union (keys m1) (keys m2)))))
Read More

Why Not a Function #3: dissoc-in

(defn dissoc-in
  [m [k & knext :as ks]]
  (cond
    (and knext
      (contains?
        (get-in m (butlast ks))
        (last ks))) (update-in m (butlast ks) dissoc (last ks))
    (not knext) (dissoc m k)
    :else m))
Read More

Why Not a Function #2: unapply

(defn unapply [f & args] (f args))

unapply is a kind of a reverse of apply from Clojure.

`apply’ lets us use a collection instead of multiple arguments:

(apply + [1 2])
=> 3

unapply lets us use multiple arguments instead of a collection:

(defn counts [coll] (map count coll))
=> #'user/counts

(unapply counts "why" "not" "a" "function")
=> (3 3 1 8)
Read More

Why Not a Function #1: unfnil

(defn unfnil
  [f]
  (fn [& args]
    (let [val (apply f args)]
      (if (empty? val)
        nil
        val))))

unfnil is a kind of a reverse of fnil from Clojure.

For example, fnil can be used with conj like this: (fnil conj #{}) which will replace the first argument to conj with #{} if it is nil. Useful if we want a set instead of a list, because conj with nil produces a list.

(update {:alarm true} :days (fnil conj #{}) :monday)
=> {:alarm true, :days #{:monday}}

unfnil is the reverse, for instance (unfnil disj) with #{:a} :a produces a nil instead of an empty set. Unlike fnil, unfnil works with collections only.

(update *1 :days (unfnil disj) :monday)
=> {:alarm true, :days nil}
Read More

Migrating from lein-figwheel to figwheel-main and keeping Cursive REPL

figwheel-main is a re-write of Figwheel, that doesn’t depend on lein-cljsbuild config and doesn’t require Leiningen at all. Migrating a Leiningen project to it is not too difficult but there may be a few gotchas, depending on your project, as figwheel-main doesn’t support all of the config options of lein-figwheel. Also, our specific case was to continue using Cursive nREPL integration - something we were enjoying with lein-figwheel.

Read More

Deep merge in Clojure with a bug

I’ve found a bug in our deep-merge fn. It was used to override default parameters but some defaults were not overwritten. This is how our deep-merge looked like:

Read More