Fork me on GitHub
#code-reviews
<
2022-08-26
>
escherize19:08:23

I wrote a cute little macro that does a threading diff: 🧵

(comment

  (-diff->
    {}
    (assoc :a 1 :b {})
    (assoc-in [:b :c] [1 2 3 4])
    (update-in [:b :c 2] inc)
    (update-in [:b :c] reverse))

  )
if you run what’s in the comment block, it’ll print out: https://clojurians.slack.com/files/U051GFP2V/F03VCU6MBQT/screen_shot_2022-08-26_at_1.47.36_pm.png

escherize19:08:43

(ns playground.thread-diff
  (:require [clojure.pprint :as pprint]
            [lambdaisland.deep-diff2 :as ddiff]
            [clojure.walk :as walk]
            [clojure.string :as str]))

(defn print-diff! [a b]
  (ddiff/pretty-print (ddiff/diff a b) (ddiff/printer {:width 80})))

(defn outerpose [x ys]
  (concat [x] (interpose x ys) [x]))

(defmacro -diff->
  "Threads the expr through the forms. Inserts x as the
  second item in the first form, making a list of it if it is not a
  list already. If there are more forms, inserts the first form as the
  second item in second form, etc."
  {:added "1.0"}
  [in-x & in-forms]
  (loop [x [::nothing in-x],
         forms (concat
                 ;; Starting:
                 [(list (list 'fn '[[old new]]
                              (list 'println "Starting -diff-> with:" '(pr-str new) "\n")
                              '[old new]))]
                 (interleave
                   ;; run with old and new context steps
                   (mapv
                     (fn [form]
                       (list (list 'fn '[[_ new]] ['new (list '-> 'new form)])))
                     in-forms)
                   ;; report diff result steps
                   (mapv (fn [form]
                           (list (list 'fn '[[old new]]
                                       (list 'println "")
                                       ;; print Running step
                                       (list 'println "Running: " (list 'str "(-> "  (list pr-str 'old) " " (pr-str form) ")"))
                                       ;; print diff
                                       '(print-diff! old new)
                                       '[old new])))
                         in-forms)))]
    (if forms
      (let [form (first forms)
            threaded (if (seq? form)
                       (with-meta `(~(first form) ~x ~@(next form)) (meta form))
                       (list form x))]
        (recur threaded (next forms)))
      (list last x))))

(comment

  (-diff->
    {}
    (assoc :a 1 :b {})
    (assoc-in [:b :c] [1 2 3 4])
    (update-in [:b :c 2] inc)
    (update-in [:b :c] reverse))

  )

escherize19:08:31

the way it works, is by wrapping the steps that do actions in functions that take 2 args: old and new.

escherize19:08:56

the wrapping functions apply a step, put its result into new, and the input to the step into old

escherize19:08:23

so if you had inc, it would basically turn into (fn [[old new]] [new (inc new)])

escherize19:08:58

then there’s a function that takes the initial value and turns it into [::nothing in-x] at the beginning and calls last at the end with (list last x)

Noah Bogart19:08:35

that's so cool and fun!

🙏 2
lread21:08:09

Yeah, neato! Thanks for sharing!

Drew Verlee03:08:05

Super cool. I assume this will be useful in every day development? Would it work to replace the threading macro with this temporarily to get more info about some context? I can't see why not...

jumar03:09:54

Are the clojure.pprint, clojure.string, and clojure.walk requires relicts from earlier experiments?

escherize03:09:34

Must be. I see that those aren’t needed here. I think I was trying to write the macro a different way

jumar03:09:57

Also, it would be great to have a version that plays nicely with large data structures, perhaps by printing just smaller portion of the diff.

jumar04:09:59

Finally, I think you can make it more general by accepting the threading symbol as an argument. So it's automatically usable for both -> and ->>. As in

(tdiff -> ...)
https://github.com/jumarko/clojure-experiments/pull/34/files#diff-8e10d07b03ac46755d48405271bfef8ecde8e64a3b4a35559f6440c8bdf88fcfR50

💯 1