gkumar7
gkumar7

Reputation: 355

Clojure Built in Function to access the entire record

(defrecord Sample (x y))

(def check (Sample. 1 2))

(:x check) ;returns 1

If I receive (:x check) as an argument to a function, is there a way to access check? Or, in other words, return

#:user.Sample{:x 1, :y 2}

Upvotes: 2

Views: 135

Answers (2)

jbm
jbm

Reputation: 2600

As Óscar described, (:x check) won't work because its result is 1, and '(:x check) won't work because its result is a list containing the keyword :x and the symbol check.

However, instead of using quote you could use the list function:

(defn receiver [arg]
  (map class arg))

;; With a quoted list it receives the symbol `check` rather
;; than the Sample record
(receiver '(:x check))
;=> (clojure.lang.Keyword clojure.lang.Symbol)

;; But this way it does receive the Sample
(receiver (list :x check))
;=> (clojure.lang.Keyword user.Sample)

And (list :x check) can be evaluated:

(eval (list :x check))
;=> 1

(defn receiver [arg]
  (str "received " arg "; eval'd to: " (eval arg)))

(receiver (list :x check))
;=> "received (:x #user.Sample{:x 1, :y 2}); eval'd to: 1"

The reason that quote and list behave so differently is that quote doesn't evaluate it's argument. And, when a list is quoted, that effect is recursive: none of the items in the list are evaluated either.

There's another type of quote, called syntax-quote or backquote (and described on the Clojure.org page about the reader) which allows you to selectively un-quote (i.e. evaluate) items.

(require '[clojure.pprint])

(let [n 1
      x :x
      c check]
  (clojure.pprint/pprint
   (vector `(n x c)
           `(~n x c)
           `(~n ~x c)
           `(~n ~x ~c))))

Prints:

[(user/n user/x user/c)
 (1 user/x user/c)
 (1 :x user/c)
 (1 :x {:x 1, :y 2})]

And, actually, I lied a little bit. You could in fact use '(:x check) in this particular case. resolve will return the Var associated with a symbol, and deref (or its @ reader macro) will get you the Var's value.

(resolve 'check)
;=> #'user/check

(deref (resolve 'check))
;=> #user.Sample{:x 1, :y 2}

(defn resolve-and-deref-symbols [form]
  (map (fn [x] (if (symbol? x)
                 @(resolve x)
                 x))
       form))

(defn receiver [arg]
  (str "received " arg "; eval'd to: " (eval (resolve-and-deref-symbols arg))))

(receiver '(:x check))
;=> "received (:x check); eval'd to: 1"

I didn't mention it straight-away because, while it works easily enough in this example, it's not at all appropriate for the general case. (For instance, it won't work with locals, and handling namespaces and nested data structure would be painful).

Upvotes: 2

Óscar López
Óscar López

Reputation: 236004

No, if a function is passed (:x check) as parameter then the value was already evaluated before entering the function, you'll just receive a 1 as value, and you can't retrieve the record it came from.

If you need the record inside the function, why don't you pass check as parameter?

Upvotes: 4

Related Questions