tags:

views:

74

answers:

2

Hi!

Is there a way to mimic a this variable in something like (def foo {:two 2 :three (inc (:two this))})? Even better would be something like (def foo {:two 2 :three (inc ::two)}). I was told that there is a library that does exactly this, but I can't really find anything similar.

Thanks!

+1  A: 

(Update: Rearranged & reworked. build-map and (a sketch of) -m> macros added.)

You could write this particular example as

(def foo (zipmap [:two :three] (iterate inc 2)))

The easiest general solution which occurs to me at this moment is

user> (-> {} (assoc :two 2) (#(assoc % :three (inc (:two %)))))
{:three 3, :two 2}

It's actually very flexible, although it does require you to write out assoc repeatedly.

To enable syntax similar to that from the question text, you could use something like this:

(defn build-map* [& kvs]
  (reduce (fn [m [k v]]
            (assoc m k (v m)))
          {}
          kvs))

(defmacro build-map [& raw-kvs]
  (assert (even? (count raw-kvs)))
  (let [kvs (map (fn [[k v]] [k `(fn [m#] (let [~'this m#] ~v))])
                 (partition 2 raw-kvs))]
    `(build-map* ~@kvs)))

user> (build-map :two 2 :three (inc (:two this)))
{:three 3, :two 2}

You could easily change this to use a user-supplied symbol rather than the hardcoded this. Or you could switch to %, which is just a regular symbol outside anonymous function literals. Maybe add an explicit initial map argument, call it -m> (for map threading) and you can do

(-m> {} :two 2 :three (inc (:two %)))

for the same result.


Another funky way (mostly for the fun):

;;; from Alex Osborne's debug-repl,
;;; see http://gist.github.com/252421
;;; now changed to use &env
(defmacro local-bindings
  "Produces a map of the names of local bindings to their values."
  []
  (let [symbols (map key &env)]
    (zipmap (map (fn [sym] `(quote ~sym)) symbols) symbols)))

(let [two 2
      three (inc two)]
  (into {} (map (fn [[k v]] [(keyword k) v]) (local-bindings))))
{:two 2, :three 3}

Note that this will also capture the bindings introduced by any outer let forms...

Michał Marczyk
+3  A: 

If you want a temporary name for something, that's what let is for.

(def foo (let [x {:two 2}]
           (assoc x :three (inc (:two x)))))

I don't know of any library that does what you want. Every once in a while, someone suggests a "generalized arrow", like -> but with a magic symbol you can stick in the intermediary expressions which will be replaced by something else. See for example here and here. But this idea tends to be shot down because it's more complex and confusing for little benefit. let is your friend. See Rich's example:

(let [x []
      x (conj x 1)
      x (into x [2 3])
      x (map inc x)]
...) 
Brian Carper
+1 for existing solution over new complex magic constructions.
Leonel