13

Ok Here is what i am trying to do

(defn addresses [person-id]
 ;addresses-retrival )

(defn person [id]
  (merge  {:addresses (addresses id)} {:name "john"}))

In the above person function i want addresses to be retrieved only on demand , like only when i do

(:addresses (person 10)) 

and not when

(person 10)

I am not sure if i am going about this right, being new to clojure.

5 Answers 5

11

You can use delay.

(defn person [id]
  (delay  {:addresses (addresses id) :name "john"})) 

(person 2) will then return a delayed, without evaluating anything. To access the content and evaluate the delayed object, use force or deref (or @).

(:addresses @(person 5))

Alternatively, you can put the delay on the address only.

(defn person [id]
  {:addresses (delay (addresses id)) :name "john"})

which can be nicer depending on your problem.

It allows to define:

(defn get-address [person]
  @(:address person))

Which will get the delayed address and force it. (Forcing means computing the first time and retrieving the forced result any other times).

Sign up to request clarification or add additional context in comments.

3 Comments

thank you so much .I was wondering if this could be made transparent so it could be evaluated first time it is used . Instead of manually forcing evaluation ?
I don't think so. Clojure is explicit in lazyness and force, as far as I know.
My lazymap library does exactly that. It provides transparent drop-ins for all map types, which compute their values only in case they are really retrieved.
1

At least as far as sequences go, clojure is pretty damned lazy without needed to be told.

Here, modelling your address-retrieval as counting, try:

(defn addresses [person-id]
  (iterate #(do (println %) (inc %)) person-id))

(defn person [id]
  (merge  {:addresses (addresses id)} {:name "john"}))

(def people (map person (range 100)))

So far it won't have printed anything, but if you say:

(doall (take 5 (:addresses (nth people 10))))

Then you should see the printing happen in exactly the cases that need to happen to count up five in the tenth place. I'd imagine that might be the sort of behaviour you want?

So get your address lookup to produce a lazy sequence (map, filter, reduce will all do)

Comments

1

You can return a function from the addresses function which when later called will retrieve the addresses. Something like this:

(defn addresses [person-id]
 #(;addresses-retrival))

(defn person [id]
  (merge  {:addresses ((addresses id))} {:name "john"}))

Note than the addresses function returns an anonymous function (created using #) and the person function calls that anonymous function using an extra pair of parens.

Comments

0

I can suggest something close to what you expect.

; Note the use of anonymouns function. #(addresses id)
(defn person [id] 
  (merge  {:addresses #(addresses id)} {:name "john"}))

; :addresses returns a function. Evaluate it by wrapping it in another set of parans.
((:addresses (person 10)))

Comments

0

Remember that Delays are memoized, so successive calls of your addresses delay will always yield the same address as the first time you derefed the Delay.

(defn addresses [person-id]
  {:home (str (rand-int 100) " Cool St.") :work "1243 Boring St."})

(defn person [id]
  (merge {:addresses (delay (addresses id))} {:name "john"}))

(let [person1 (person 1)]
  (println @(:addresses person1))
  (println @(:addresses person1)))

This will print:

{:home 65 Cool St., :work 1243 Boring St.}
{:home 65 Cool St., :work 1243 Boring St.}

Notice how the home address is unchanged on the second deref of the delay.

If you don't want this behavior you need to use a function closure instead.

(defn addresses [person-id]
  {:home (str (rand-int 100) " Cool St.") :work "1243 Boring St."})

(defn person [id]
  (merge {:addresses (fn [] (addresses id))} {:name "john"}))

(let [person1 (person 1)]
  (println ((:addresses person1)))
  (println ((:addresses person1))))

This will print:

{:home 16 Cool St., :work 1243 Boring St.}
{:home 31 Cool St., :work 1243 Boring St.}

Notice how the home address was different on the sub-sequent call to the closure.

So, if you're addresses function does side-effect, say fetches addresses from a database. And the persons can change their addresses, and you'd want your code to always have the most recent address, it's something to keep in mind if Delay works for you, or if a function closure would be a better candidate.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.