small medium large xlarge

Generic-user-small
24 Jun 2015, 16:57
Stig Brautaset (2 posts)
  • maybe? is not defined (though trivially implemented)
  • store/init doesn’t take an argument, but store/init-with-restock does. (Should this function perhaps replace store/init in the shopping.store ns? That’s the approach I’ve taken below.)
  • report is defined after its use, causing a compile error unless it’s moved.
  • init (called from go-shopping) is not defined.
  • The creation & initialisation of the kids channel is never shown. This is where it falls apart for me.

Here’s my attempt:

(ns shopping.family-async
  (:require [clojure.core.async :refer [>!! >! <! go go-loop chan]]
            [shopping.store :as store]))

(def my-kids #{:alice :bobby :cindy})

(defn born! [new-kid]
  (alter-var-root #'my-kids conj new-kid))

(def shopping-list (ref #{}))
(def assignments (ref {}))
(def shopping-cart (ref #{}))

(defn notify-parent
  [k r _ nv]
  (if (contains? nv :candy)
    (println "there's candy in the cart!")))


(defn init []
  (store/init {:eggs 2 :bacon 3 :apples 3
               :candy 5 :soda 2 :milk 1
               :bread 3 :carrots 1 :potatoes 1
               :cheese 3})

  (dosync
   (ref-set shopping-list #{:milk :butter :bacon :eggs
                            :carrots :potatoes :cheese :apples})
   (ref-set assignments #{})
   (ref-set shopping-cart #{}))
  (add-watch shopping-cart :candy notify-parent))


(defn maybe?
  "Do 'f' 1/3 of the time"
  [f]
  (if (= 0 (rand-int 3))
    (f)))

(defn assignment [child]
  (get-in @assignments child))

(defn buy-candy []
  (dosync
   (commute shopping-cart conj (store/grab :candy))))

(defn collect-assignment [child]
  (let [item (assignment child)] (dosync
                                  (alter shopping-cart conj item)
                                  (alter assignments dissoc child)
                                  (ensure shopping-list) ;; not needed
                                  ) item))

(defn assign-item-to-child [child]
  ;; included as an example
  (dosync
   (alter assignments assoc child (first @shopping-list))
   (alter shopping-list disj (assignment child)))
  (assignment child))

(defn dawdle
  "screw around, get lost, maybe buy candy" []
  (let [t (rand-int 5000)]
    (Thread/sleep t)
    (maybe? buy-candy)))

(defn send-child-for-item
  "eventually shop for an item"
  [child item q]
  (println child "is searching for" item)
  (dawdle)
  (collect-assignment child)
  (>!! q child))


(defn report []
  (println "store inventory" @store/inventory)
  (println "shopping-list" @shopping-list)
  (println "assignments" @assignments)
  (println "shopping-cart" @shopping-cart))

(defn go-shopping []
  (init)
  (report)
  (let [kids (chan 10)]
    (doseq [k my-kids]
      (>!! kids k))

    (go-loop
        [kid (<! kids)]
      (if (not (empty? @shopping-list))
        (do (go
              (send-child-for-item kid (assign-item-to-child kid) kids))
            (recur (<! kids)))
        (println "done shopping."))))
  (report))

When I run it this is what I’m seeing:

user> (family/go-shopping)
need to restock :butter
store inventory {:milk 1, :apples 3, :candy 5, :bread 3, :bacon 3, :cheese 3, :potatoes 1, :carrots 1, :soda 2, :eggs 2}
shopping-list #{:milk :apples :butter :bacon :cheese :potatoes :carrots :eggs}
assignments #{}
shopping-cart #{}
store inventory {:milk 1, :apples 3, :candy 5, :bread 3, :bacon 3, :cheese 3, :potatoes 1, :carrots 1, :soda 2, :eggs 2}
shopping-list #{:milk :apples :butter :bacon :cheese :potatoes :carrots :eggs}
assignments #{}
shopping-cart #{}
;; => nil
Generic-user-small
27 Jun 2015, 11:51
Stig Brautaset (2 posts)

After a bit more debugging I’ve managed to get it to work. The reason for my problem wasn’t the initialisation of the kids channel as I thought, but that the assignment defn should use get rather than get-in (reported separately). In the form it is in the book it throws an exception, but because that’s happening on a separate thread I just saw a silent failure.

Here’s my complete (working) example, to show my modifications.

(ns shopping.family-async
  (:require [clojure.core.async :refer [>!! >! <! go go-loop chan]]
            [shopping.store :as store]))

(def my-kids #{:alice :bobbi :cindy})

(defn born! [new-kid]
  (alter-var-root #'my-kids conj new-kid))

(def shopping-list (ref #{}))
(def assignments (ref {}))
(def shopping-cart (ref #{}))

(defn notify-parent
  [k r _ nv]
  (if (contains? nv :candy)
    (println "there's candy in the cart!")))


(defn init []
  (store/init {:eggs 2 :bacon 3 :apples 3
               :candy 5 :soda 2 :milk 1
               :bread 3 :carrots 1 :potatoes 1
               :cheese 3 :butter 3 })

  (dosync
   (ref-set shopping-list #{:milk :butter :bacon :eggs
                            :carrots :potatoes :cheese :apples})
   (ref-set assignments {})
   (ref-set shopping-cart #{}))
  (add-watch shopping-cart :candy notify-parent))


(defn maybe?
  "Do 'f' 1/3 of the time"
  [f]
  (if (= 0 (rand-int 3))
    (f)))

(defn assignment [child]
  (get @assignments child))

(defn buy-candy []
  (dosync
   (commute shopping-cart conj (store/grab :candy))))

(defn collect-assignment [child]
  (let [item (assignment child)] (dosync
                                  (alter shopping-cart conj item)
                                  (alter assignments dissoc child)
                                  (ensure shopping-list) ;; not needed
                                  ) item))

(defn assign-item-to-child [child]
  (let [item (first @shopping-list)]
    (dosync
     (alter assignments assoc child item)
     (alter shopping-list disj item))
    item))

(defn dawdle
  "screw around, get lost, maybe buy candy"
  []
  (let [t (rand-int 5000)]
    (Thread/sleep t)
    (maybe? buy-candy)))

(defn send-child-for-item
  "eventually shop for an item"
  [child item q]
  (println child "is searching for" item)
  (dawdle)
  (collect-assignment child)
  (>!! q child))


(defn report []
  (println "store inventory" @store/inventory)
  (println "shopping-list" @shopping-list)
  (println "assignments" @assignments)
  (println "shopping-cart" @shopping-cart))

(defn go-shopping []
  (init)
  (report)
  (let [kids (chan 10)]
    (doseq [k my-kids]
      (>!! kids k))

    (go-loop [kid (<! kids)]
      (if (not (empty? @shopping-list))
        (do (go
              (send-child-for-item kid (assign-item-to-child kid) kids))
            (recur (<! kids)))
        (do (println "done shopping.")
            (report))))))
Alex_head_pragsmall
29 Jun 2015, 11:16
Alex Miller (23 posts)

Thanks Stig, we will take a look! - Alex

Current-profile (1)_pragsmall
04 Jul 2015, 22:08
Ben Vandgrift (3 posts)

Stig,

I used most of your suggestions; this had a ripple effect into other places as well, and prompted some cleanup. All in all, nice work.

Many thanks!

–ben

You must be logged in to comment