2010/8/5 Kyle Schaffrick <k...@raidi.us> > On Thu, 5 Aug 2010 16:05:07 +0200 > Laurent PETIT <laurent.pe...@gmail.com> 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 clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en