I do a lot of computation in let
blocks, returning hash-maps containing the data. The following is a not-so-minimal example:
(def ground-truth
(let [n 201
t1 2.0
linspace (fn [a b n] (let [d (/ (- b a) (dec n))]
(map (fn [x] (+ a (* x d))) (range n))))
times (vec (linspace 0.0, t1, n))
wavelength 1
wavespeed 1
f (* (/ wavespeed wavelength) 2 Math/PI)
dt (- (get times 1) (get times 0))
amplitude 5.0
ground-level 10.0
h-true (mapv #(+ ground-level
(* amplitude (Math/sin (* f %))))
times)
h-dot-true (mapv #(* amplitude f (Math/cos (* f %)))
times)
baro-bias-true -3.777]
{:n n, :times times, :f f, :dt dt, :h-true h-true,
:h-dot-true h-dot-true, :baro-bias-true baro-bias-true}))
What I want to do is get rid of the repetition in the final expression. It's not such a big problem for this little example, but I have some that are much longer and more elaborate and the repetition makes modifying the expression tedious and error-prone.
I tried this macro:
(defmacro hashup [name-list]
`(into {}
(map vector
(mapv keyword ~name-list)
(mapv eval ~name-list))))
which works only if eval
works, which works on vars
:
(def foo 41) (def bar 42)
(hashup '[foo bar])
{:foo 41, :bar 42}
but not on let
blocks:
(let [a 1, b (inc a)] (hashup '[a b]))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: a in this context, compiling:(null:1:1) Util.java: 221 clojure.lang.Util/runtimeException
core.clj: 3105 clojure.core$eval/invokeStatic
as expected after reviewing the following SO questions, amongst many others: Variable scope + eval in Clojure, eval a list into a let on clojure
One might say "well, you can either have repetition outside your let
blocks by def
fing your variables in namespaces and then using something like hashup
, or you can have your repetition at the bottom of your let
blocks and forget about macro magic. But there is no way to Don't Repeat Yourself in this exact use case.
Did I miss a good way to DRY out this kind of code?
fnk
from github.com/plumatic/plumbing – cfrickeval
explicitly. – Rördvars
is never evaluated. The reflex of prefixing every macro with a backtick to suspend evaluation induces a distraction: the evaluation it's suspending is rewrite-time evaluation, which should not be conflated with run-time evaluation. – Reb.Cabin