user1311390
user1311390

Reputation:

Clojure: defRecord, defProtocol: doing expensive calculation only once

Context

Consider the following piece of code

(defprotocol ICat "Foo"
  (meow [cat]))

(defrecord Cat [a b] "Cat"
  ICat
  (meow [cat] (some-expensive-operation a b)))

Question

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

Answers (3)

mikera
mikera

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

user1311390
user1311390

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

ideally_world
ideally_world

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

Related Questions