How to memoize a function that uses reading core.async and non-blocking channel?

I would like to use memoizefor a function that uses core.asyncand <!eg

(defn foo [x]
  (go
    (<! (timeout 2000))
    (* 2 x)))

(In real life, this can be useful for caching server call results)

I was able to achieve this by writing core.async version of memoize (almost the same code as memoize):

(defn memoize-async [f]
  (let [mem (atom {})]
    (fn [& args]
      (go
        (if-let [e (find @mem args)]
          (val e)
         (let [ret (<! (apply f args))]; this line differs from memoize [ret (apply f args)]
            (swap! mem assoc args ret)
            ret))))))

Usage example:

(def foo-memo (memoize-async foo))
(go (println (<! (foo-memo 3)))); delay because of (<! (timeout 2000))

(go (println (<! (foo-memo 3)))); subsequent calls are memoized => no delay

I am wondering if there are simpler ways to achieve the same result.

** Note. I need a solution that works with <!. For <!!see this question: How to memoize a function using the core.async channel and the blocking channel? **

+4
source share
2

memoize. , :

 (defn wait-for [ch]
      (<!! ch))

, <!!, <!, , . <! go.

memoized , foo, :

(def foo-memo (memoize (comp wait-for foo)))

foo , wait-for , (.. foo).

foo-memo , , <!, wait-for :

(go (println (foo-memo 3))

go, , (.. , foo).

+1

, . , , memoized , , go, . core.async.

core.async pub/sub ( CLJS):

(def lookup-sentinel  #?(:clj ::not-found :cljs (js-obj))
(def pending-sentinel #?(:clj ::pending   :cljs (js-obj))

(defn memoize-async
  [f]
  (let [>in (chan)
        pending (pub >in :args)
        mem (atom {})]
    (letfn
        [(memoized [& args]
           (go
             (let [v (get @mem args lookup-sentinel)]
               (condp identical? v
                 lookup-sentinel
                 (do
                   (swap! mem assoc args pending-sentinel)
                   (go
                     (let [ret (<! (apply f args))]
                       (swap! mem assoc args ret)
                       (put! >in {:args args :ret ret})))
                   (<! (apply memoized args)))
                 pending-sentinel
                 (let [<out (chan 1)]
                   (sub pending args <out)
                   (:ret (<! <out)))
                 v))))]
        memoized)))

: , , <out

0

Source: https://habr.com/ru/post/1548066/


All Articles