Reputation:
Consider the following piece of code
(defprotocol ICat "Foo"
(meow [cat]))
(defrecord Cat [a b] "Cat"
ICat
(meow [cat] (some-expensive-operation a b)))
Is there a way I can throw a let somewhere into there?
I would prefer that (some-expensive-operation a b) is evaluated exactly once, at the time I execute
(->Cat a b)
so that at the time of (meow cat), it just returns the pre-cached value, rather than recalculate it on the fly. So for example:
[1] (let [x (->Cat a b)]
[2] (meow x)
[3] (meow x)
[4] (meow x))
I want (some-expensive-operation) to be evaluated exactly once at [1], then for [2], [3], [4] it just returns the old value.
Upvotes: 5
Views: 521
Reputation: 106351
I'd suggest wrapping the logic to call the expensive operation once in a constructor function, and storing the result as a regular value in the record:
(defprotocol ICat "Foo"
(meow [cat]))
(defrecord Cat [a b] "Cat"
ICat
(meow [cat] (:meow cat)))
(defn make-cat [a b]
(assoc (->Cat a b) :meow (some-expensive-operation a b)))
When your code gets more complex I find that you often want to define your own constructor functions in any case.
Note that you might also want to consider wrapping the expensive operation in a lazy sequence or a delay so that it only gets computed if needed.
Upvotes: 3
Reputation:
I'm starting to believe that the simplest way around this may be to
create a wrapper around defrecord
this wrapper allows me to specify other fields that I want to recalculate
and attach these extra fields to the "real" defrecord
Upvotes: 0
Reputation: 436
If you function is referentially transparent, then you could wrap your function in memoize
. At a minimum, you could:
(def memo-some-expensive-function (memoize some-expensive-function))
and then use memo-some-expensive-function
in your record.
Upvotes: 1