My efforts to port http://www.youtube.com/watch?v=f6kdp27TYZs to Clojure.
func boring(msg string) {
for i := 0; ; i++ {
fmt.Println(msg, i)
time.Sleep(time.Second)
}
}
(defn boring [msg]
(loop [i 0]
(println msg i)
(Thread/sleep 1000)
(recur (inc i))))
Output:
(boring "boring!")
; boring! 0
; boring! 1
; boring! 2
; boring! 3
; ...
Added random sleep interval of 1 to 1000ms.
func boring(msg string) {
for i := 0; ; i++ {
fmt.Println(msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}
func main() {
boring("boring!")
}
(defn boring [msg]
(loop [i 0]
(println msg i)
(Thread/sleep (rand-int 1000))
(recur (inc i))))
(defn -main [& args]
(boring "boring!"))
main
runs forever.
func main() {
go boring("boring!")
}
(defn -main [& args]
(go (boring "boring!")))
main
now returns immediately.
Now, while the goroutine is executing concurrently, we sleep for two seconds before exiting.
func main() {
go boring("boring!")
fmt.Println("I'm listening.")
time.Sleep(2 * time.Second)
fmt.Println("You're boring; I'm leaving.")
}
(defn -main [& args]
(go (boring "boring!"))
(println "I'm listening.")
(Thread/sleep 2000)
(println "You're boring; I'm leaving."))
Output:
I'm listening.
boring! 0
boring! 1
boring! 2
boring! 3
boring! 4
You're boring; I'm leaving.
func boring(msg string, c chan string) {
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i) // Expression to be sent can be any val.
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}
func main() {
c := make(chan string)
go boring("boring!", c)
for i := 0; i < 5; i++ {
fmt.Printf("You say: %q\n", <-c) // Receive expression is just a value.
}
fmt.Println("You're boring; I'm leaving")
}
(defn boring [msg c]
(loop [i 0]
(>!! c (str msg " " i))
(recur (inc i))))
(defn -main [& args]
(let [c (chan)]
(go (boring "boring!" c))
(dotimes [_ 5]
(println (<!! c)))
(println "You're boring; I'm leaving.")))
func boring(msg string) <-chan string { // Returns receive-only channel of strs.
c := make(chan string)
go func() { // We launch the goroutine from inside the function.
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c // Return channel to the caller
}
func main() {
c:= boring("boring!") // Function returning a channel.
for i := 0; i < 5; i++ {
fmt.Printf("You say: %q\n", <-c)
}
fmt.Println("You're boring; I'm leaving.")
}
(defn boring [msg]
(let [c (chan)]
(go (loop [i 0]
(>! c (str msg " " i))
(Thread/sleep (rand-int 1000))
(recur (inc i))))
c))
(defn -main [& args]
(let [c (boring "boring!")]
(dotimes [_ 5]
(println (<!! c)))
(println "You're boring; I'm leaving.")))
func main() {
joe := boring("Joe")
ann := boring("Ann")
for i := 0; i < 5; i++ {
fmt.Println(<-joe)
fmt.Println(<-ann)
}
fmt.Println("You're both boring; I'm leaving.")
}
(defn -main [& args]
(let [joe (boring "Joe")
ann (boring "Ann")]
(dotimes [_ 5]
(println (<!! joe))
(println (<!! ann)))
(println "You're both boring; I'm leaving.")))
func fanIn(input1, input2 <-chan string) <-chan string {
c := make(chan string)
go func() { for { c <- <-input1 } }()
go func() { for { c <- <-input2 } }()
return c
}
func main() {
c := fanIn(boring("Joe"), boring("Ann"))
for i:= 0; i < 10; i++ {
fmt.Println(<-c)
}
fmt.Println("You're both boring; I'm leaving.")
}
I improved the Clojure version of fan-in
by letting it accept any number of channels. It'll sequentially iterate over them forever.
(defn fan-in [& ports] ; joe ->\___c
(let [c (chan)] ; ann ->/
(go (doseq [port (cycle ports)]
(>! c (<! port))))
c))
(defn -main [& args]
(let [c (fan-in (boring "Joe")
(boring "Ann"))]
(dotimes [_ 10]
(println (<!! c)))
(println "You're both boring; I'm leaving.")))
Reading off of channels is fine, reading off an arbitrary JVM socket is not, as that will block the entire thread. The way this should be performed is thus:
spin up a (thread) block for every JVM socket (or groups of sockets), and then create core.async channels for each socket.
have the thread put data either via >!! or put! into the channel.
service the channels via (go) blocks.
Use thread for IO bound tasks, go for CPU bound tasks. With this sort of model you'll find that most of your application will end up looking like a core of go blocks that are surrounded by "side-effecting" thread blocks. Keep IO operations at the edges of your application and do just enough to get the data into channels. Then do everything else with go blocks and <! and >!.