Search code examples
clojure

Can I refer to a clojure hashmap value from another value in the same map?


I'm trying to come up with some way for the values in a clojure hashmap to refer to each other. Conceptually, something like this:

(def m {:a 1 :b 5 :c (+ (:a m) (:b m))}  ;Implies (= (:c m) 6)

This doesn't work, of course since I'm circularly referencing m. I can do something like

(def m {:a 1 :b 5 :c (fn [a b] (+ a b))})
((:c m) (:a m) (:b m)) ;=> 6

but that doesn't really gain anything because I still have to know which a and b to put into the function. Another attempt:

(def m {:a 1 :b 5 :c (fn [m] (+ (:a m) (:b m)))})
((:c m) m) ;=> 6

It's a bit better since I've now internalized the function to a map though not specifically this map. I might try to fix that with something like this

(defn new-get [k m]
  (let [v-or-fn (get m k)]
    (if (fn? v-or-fn) (v-or-fn m) v-or-fn)))

(def m {:a 1 :b 5 :c (fn [m] (+ (:a m) (:b m)))})
(new-get :a m) ;=> 1
(new-get :b m) ;=> 5
(new-get :c m) ;=> 6

I think this is about the best I can do. Am I missing something more clever?


Solution

  • As I've already said in comment above you can use let form:

    (def m 
      (let [a 1 b 5] 
        {:a a :b b :c (+ a b)}))
    

    This should be fine if you're using values that known only inside m definition. Otherwise you would better to use function parameters as @Michiel shown.

    P.S. by the way you're free to use everything inside def you're usually use in clojure. Moreover, sometimes you're free to use let in sugared form inside some other forms (although this let uses different mechanisms than usual let form):

    (for [x (...) xs]
         :let [y (+ x 1)]
         ; ...