2010/8/5 Kyle Schaffrick <[email protected]>
> On Thu, 5 Aug 2010 16:05:07 +0200
> Laurent PETIT <[email protected]> wrote:
> >
> > My point was that by providing different interfaces/protocols to
> > different "users", it's more an implementation detail than anything
> > else if they have the same object or not. I don't expect my users to
> > program on types, but on protocols/interfaces.
> >
> > (ok, in this case, i'm my own user, so I have indeed some expectations
> > on me ;) )
> >
> >
> > > > After all I was wrong, it's not a delayed delay I've written.
> > > > How to rename things ?
> > > >
> > > > timed-delay -> ?
> > > > delay.util.Cancellable -> ?
> > > > isCancelled -> ?
> > > > cancel -> ?
> > >
> > > timed-delay -> ok
> > > Cancellable -> Timed
> > > isCancelled -> countdown-stopped?
> > > cancel -> stop-countdown
> > >
> > > Maybe like this? I dunno. I'm bad at names. :]
> > >
> > >
> > Hm, well .. let's say not worse than me :)
> >
>
> It sounds like you're having trouble defining what the primitives of
> your interface are. If I may attempt it, I'll call this construct an
> "idle speculative cache", i.e. you want to speculatively cache the
> evaluation some potentially expensive function over an input at a time
> when the input's value is not changing in a volatile manner, thus
> avoiding repeatedly and rapidly invalidating cached results of a
> potentially expensive operation.
>
> I can identify three or possibly four useful primitives on an idle
> speculative cache:
>
> (create-isc f timeout inital-input)
> (update-input! isc x)
> (deref isc)
>
> * 'create returns some opaque thing representing the evaluator, given
> the function it is to evaluate, the idle timeout for speculative
> evaluation, and the initial input value.
> * 'update-input! updates the input value and resets the timeout.
> * 'deref gets the output value, forcing an evaluation if the cached
> output is out of date.
>
> You might optionally implement another primitive for inspecting the
> state that does not force, that can either retrieve a possibly-stale
> result, or retrieve a fresh result and fail if it would require forcing.
>
> Given that interface, here's one implementation I came up with:
>
>
> (defn- update-cache [isc]
> (let [input @(:input isc)
> new-output-val ((:func isc) (:val input))
> new-output {:val new-output-val :ts (:ts input)}]
> (reset! (:output isc) new-output)
> new-output-val))
>
> (defn- isc-runner [_ isc]
> (let [input @(:input isc)
> quiescent-time (- (System/currentTimeMillis) (:ts input))
> sleep-to-go (- (:timeout isc) quiescent-time)]
> (if (pos? sleep-to-go)
> (do (Thread/sleep sleep-to-go) (recur nil isc))
> (do (update-cache isc) false))))
>
> (defn- deref-isc [isc]
> (let [output @(:output isc)]
> (if (= (:ts @(:input isc))
> (:ts output))
> (:val output)
> (update-cache isc))))
>
> (defrecord IdleSpeculativeCache [func input output timeout runner]
> clojure.lang.IDeref
> (deref [isc] (deref-isc isc)))
>
> (defn update-input! [isc x]
> (reset! (:input isc) {:val x :ts (System/currentTimeMillis)})
> (when (not @(:runner isc))
> (send-off (:runner isc) (constantly true))
> (send-off (:runner isc) isc-runner isc)) nil)
>
> (defn create-isc [f in to]
> (let [isc (IdleSpeculativeCache.
> f
> (atom {:val nil :ts 0})
> (atom {:val nil :ts 0})
> to
> (agent false))]
> (update-input! isc in)
> isc))
>
>
> This one has one bug I know of, which is that the speculative
> evaluator ('runner which monitors the i.s.c. input activity) does not
> check if the output is already fresh due to 'deref forcing before
> re-evaluating, but I think that could be fixed easily with a test in
> update-cache.
>
> I hope it helps, :)
>
No offense, but ... are you serious ?
Seriously, seeing all those intermingled derefs, reset!, send-off, :input,
:output in such a little code base gives me a very bad smell.
I still prefer my own version, repeated here for the record :
(ns delay.util)
(defprotocol Cancellable (isCancelled [this]) (cancel [this]))
(defn timed-delay [pause fun]
(let [d (delay (fun))
f (future (Thread/sleep pause) @d)]
(reify
clojure.lang.IDeref
(deref [_] @d)
delay.util.Cancellable
(isCancelled [_] (future-cancelled? f))
(cancel [_] (future-cancel f)))))
-> the deref is on a delay, so it's causing no harm.
-> the code does not try to do "too much at once" : it only deals with one
value, letting the client decide if the succession of values should be
stored in an atom, in a ref, etc.
Still not perfect for the names, but currently quite satisfied by the
feature/code lines and feature/code simplicity ratios :-)
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en