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

Reply via email to