├── src ├── logger.clj ├── tut001.clj ├── tut100.clj ├── tut002.clj ├── tut003.clj ├── tut102.clj ├── tut101.clj ├── tut106.go ├── tut104.clj ├── sieve.clj ├── tut200.clj ├── tut004.clj ├── tut105.clj ├── tut103.clj ├── sieve.go ├── tut106.clj ├── tuthttp.clj ├── tut107.clj ├── tut005.clj ├── tut006.clj ├── tut201.clj └── tut300.clj ├── .gitignore ├── README.md └── project.clj /src/logger.clj: -------------------------------------------------------------------------------- 1 | (ns logger) 2 | 3 | (def ^:private log-agent (agent 0)) 4 | 5 | (defn log [& strs] 6 | (send log-agent (fn [ctr] (println (apply str (interpose " " strs))) (inc ctr)))) 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /lib 3 | /classes 4 | /checkouts 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | .lein-deps-sum 10 | .lein-failures 11 | .lein-plugins 12 | .lein-repl-history 13 | .nrepl-port -------------------------------------------------------------------------------- /src/tut001.clj: -------------------------------------------------------------------------------- 1 | ;; http://tour.golang.org/#62 2 | 3 | (ns tut1 4 | (:require [clojure.core.async :refer [go timeout ! ch x) 11 | (recur y (+ x y) (inc ctr))) 12 | (close! ch)))) 13 | 14 | (let [ch (chan 10)] 15 | (fib-n 20 ch) 16 | (loop [v (! ! ch (! ch (! ! ch (str msg i)) 10 | (! ! ch msg))) 13 | (recur)) 14 | ch)) 15 | 16 | (let [ch (fan-in (tut101/boring "Joe") (tut101/boring "Ann"))] 17 | (dotimes [_ 10] 18 | (println (! ! out i)) 10 | (recur (! ch i) (recur (inc i))) 14 | 15 | (loop [ch ch, i 100] 16 | (when (pos? i) 17 | (let [prime (! >!! ! table (inc ball)) 12 | (recur)))) 13 | 14 | (let [table (chan)] 15 | (player "ping" table) 16 | (player "pong" table) 17 | 18 | (>!! table 0) ;; go-lang will detect a deadlock if this commented out 19 | (Thread/sleep 1000) 20 | (! quit-ch 0)) 20 | 21 | ;; note that fib-q go 'process' are created after the consumer above 22 | 23 | (fib-q res-ch quit-ch) 24 | ) 25 | -------------------------------------------------------------------------------- /src/tut105.clj: -------------------------------------------------------------------------------- 1 | ;; http://talks.golang.org/2012/concurrency.slide#38 2 | 3 | (ns tut105 4 | (:require [clojure.core.async :refer [go-loop timeout >! >!! ! quit-ch "See you!") 12 | (do 13 | (>! ch (str msg i)) 14 | (recur (inc i)))))) 15 | ch)) 16 | 17 | (let [quit (chan) 18 | joe (boring "Joe" quit)] 19 | (dotimes [_ 5] 20 | (println (!! quit :stop) 23 | (println "Joe says" (! >!! ! ch {:str (str msg i) :wait wait}) 12 | (!! (:wait msg1) :foo) 24 | (>!! (:wait msg2) :bar))) 25 | (println "You're both boring, I'm leaving")) 26 | -------------------------------------------------------------------------------- /src/sieve.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Send the sequence 2, 3, 4, ... to channel 'ch'. 6 | func Generate(ch chan<- int) { 7 | for i := 2; ; i++ { 8 | ch <- i // Send 'i' to channel 'ch'. 9 | } 10 | } 11 | 12 | // Copy the values from channel 'in' to channel 'out', 13 | // removing those divisible by 'prime'. 14 | func Filter(in <-chan int, out chan<- int, prime int) { 15 | for { 16 | i := <-in // Receive value from 'in'. 17 | if i%prime != 0 { 18 | out <- i // Send 'i' to 'out'. 19 | } 20 | } 21 | } 22 | 23 | // The prime sieve: Daisy-chain Filter processes. 24 | func main() { 25 | ch := make(chan int) // Create a new channel. 26 | go Generate(ch) // Launch Generate goroutine. 27 | for i := 0; i < 100; i++ { 28 | prime := <-ch 29 | fmt.Println(prime) 30 | ch1 := make(chan int) 31 | go Filter(ch, ch1, prime) 32 | ch = ch1 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/tut106.clj: -------------------------------------------------------------------------------- 1 | ;; http://talks.golang.org/2012/concurrency.slide#39 2 | 3 | (ns tut106 4 | (:require [clojure.core.async :refer [go >! >!! ! left (inc (!! rightmost 1) 19 | (println (!]] 3 | [clj-http.client] 4 | [org.httpkit.client] 5 | [clojure.xml :as xml] 6 | [clojure.zip :as zip])) 7 | 8 | (defn blocking-get [url] 9 | (clj-http.client/get url)) 10 | 11 | (defn async-get [result url] 12 | (org.httpkit.client/get url #(go (>! result %)))) 13 | 14 | (defn get-blog-entries [data] 15 | (letfn [(zip-str [s] 16 | (zip/xml-zip (xml/parse (java.io.ByteArrayInputStream. (.getBytes s))))) 17 | (get-data [c] 18 | (let [get-content (fn [tag] (->> c (filter #(= tag (:tag %))) first :content first))] 19 | {:id (get-content :id) 20 | :title (get-content :title) 21 | :body (get-content :content)}))] 22 | (->> data 23 | :body 24 | zip-str 25 | zip/children 26 | (filter #(= :entry (:tag %))) 27 | (map :content) 28 | (map get-data)))) 29 | -------------------------------------------------------------------------------- /src/tut107.clj: -------------------------------------------------------------------------------- 1 | ;; http://talks.golang.org/2012/concurrency.slide#50 2 | ;; http://swannodette.github.io/2013/07/12/communicating-sequential-processes/ 3 | 4 | (ns tut107 5 | (:require [clojure.core.async :refer [go go-loop timeout >! >!! ! ch [kind query])))) 12 | 13 | (def web1 (fake-search :web1)) 14 | (def web2 (fake-search :web2)) 15 | (def image1 (fake-search :image1)) 16 | (def image2 (fake-search :image2)) 17 | (def video1 (fake-search :video1)) 18 | (def video2 (fake-search :video2)) 19 | 20 | (defn fastest [query & replicas] 21 | (let [ch (chan)] 22 | (doseq [replica replicas] 23 | (replica ch query)) 24 | ch)) 25 | 26 | (defn google [query] 27 | (let [ch (chan)] 28 | (go (>! ch (! ch (! ch (! ch (:value t)) 12 | (! alts!!]])) 5 | 6 | (defprotocol Fetcher 7 | (fetch [this url])) 8 | 9 | (defn crawl [url depth fetcher ch] 10 | (let [seen (atom #{}) 11 | crawler (fn crawler [url depth] 12 | (go 13 | (when (and (pos? depth) (not (@seen url))) 14 | (swap! seen conj url) 15 | (when-let [res (fetch fetcher url)] 16 | (>! ch (assoc res :url url)) 17 | (doseq [u (:urls res)] 18 | (crawler u (dec depth)))))))] 19 | (crawler url depth))) 20 | 21 | (defrecord FakeFetcher [] 22 | Fetcher 23 | (fetch [_ url] 24 | ({"http://golang.org/" 25 | {:body "The Go Programming Language" 26 | :urls ["http://golang.org/pkg/" 27 | "http://golang.org/cmd/"]} 28 | 29 | "http://golang.org/pkg/" 30 | {:body "Packages" 31 | :urls ["http://golang.org/" 32 | "http://golang.org/cmd/" 33 | "http://golang.org/pkg/fmt/" 34 | "http://golang.org/pkg/os/"]} 35 | 36 | "http://golang.org/pkg/fmt/" 37 | {:body "Package fmt" 38 | :urls ["http://golang.org/" 39 | "http://golang.org/pkg/"]} 40 | 41 | "http://golang.org/pkg/os/" 42 | {:body "Package os" 43 | :urls ["http://golang.org/" 44 | "http://golang.org/pkg/"]}} 45 | url))) 46 | 47 | (let [ch (chan)] 48 | (crawl "http://golang.org/" 4 (->FakeFetcher) ch) 49 | (loop [] 50 | (let [tmo (timeout 500) 51 | [msg chan] (alts!! [tmo ch])] 52 | (if (= chan tmo) 53 | (println "done") 54 | (do 55 | (println "Found" (:url msg) (:body msg)) 56 | (recur)))))) 57 | -------------------------------------------------------------------------------- /src/tut201.clj: -------------------------------------------------------------------------------- 1 | ;; http://talks.golang.org/2013/advconc.slide#14 (and later) 2 | 3 | (ns tut201 4 | (:require [clojure.core.async :refer [go timeout >! >!! > updates (map :id) (into #{}))] 18 | (let [query [res-ch quit-ch (timeout interval)] 19 | query (if-not (nil? fst) (conj query [update-ch fst]) ;; potentially append the put query 20 | query) 21 | [msg ch] (alts! query)] 22 | (cond 23 | (= ch quit-ch) (close! update-ch) 24 | (= ch update-ch) (recur rst seen-ids) 25 | (= ch res-ch) (let [entries (get-blog-entries msg) 26 | new-entries (remove #(seen-ids (:id %)) entries)] 27 | (log "got" (count entries) ": new" (count new-entries) ":" url) 28 | (recur (take max-pending (concat updates new-entries)) 29 | (union seen-ids (->> new-entries (map :id) (into #{}))))) 30 | :else (do 31 | (async-get res-ch url) 32 | (recur updates seen-ids))))) 33 | (log url "subscriber quitting")) 34 | [update-ch quit-ch])) 35 | 36 | (defn fan-in [& ins] 37 | (let [ch (chan)] 38 | (doseq [i ins] 39 | (go (loop [v (! ch v)) 41 | (recur (!! qc true))) 55 | -------------------------------------------------------------------------------- /src/tut300.clj: -------------------------------------------------------------------------------- 1 | ;; http://golang.org/doc/codewalk/sharemem/ 2 | 3 | (ns tut300 4 | (:require [clojure.core.async :refer [go go-loop timeout >! >!! statuses 15 | (assoc-in [(:url msg) :status] (:status msg)) 16 | (update-in [(:url msg) :count] (fnil inc 0)))) 17 | 18 | (= ch updates) :stop 19 | 20 | :else 21 | (do 22 | (doseq [s statuses] (log s)) 23 | (recur statuses))))) 24 | updates)) 25 | 26 | (defn poller [in out status] 27 | (go-loop [] 28 | (let [resource (! status (assoc res :url (:url resource)))) 37 | (>! out resource) 38 | (recur))))) 39 | 40 | (defn wait-and-schedule [resource out] 41 | (go 42 | (log "waiting for" (:url resource)) 43 | (! out resource))) 46 | 47 | (def run (atom true)) 48 | 49 | (let [urls ["http://www.google.com/" 50 | "http://golang.org/" 51 | "http://blog.golang.org/"] 52 | pending (chan) 53 | complete (chan) 54 | status (state-monitor 5000)] 55 | 56 | (dotimes [_ 2] 57 | (poller pending complete status)) 58 | 59 | ;; create and seed resources 60 | (go 61 | (doseq [u urls] 62 | ;; we use an atom here to simulate handing over mutable state 63 | (>! pending {:url u :error-count (atom 0)}))) 64 | 65 | (go-loop [] 66 | (when @run 67 | (let [[resource ch] (alts! [complete (timeout 1000)])] 68 | (when (= ch complete) 69 | (wait-and-schedule resource pending)) 70 | (recur))))) 71 | 72 | ;; (reset! run false) 73 | --------------------------------------------------------------------------------