Skip to content

Instantly share code, notes, and snippets.

@xyproto
Forked from danneu/golang-vs-clojure-async.md
Last active April 24, 2023 09:26
Show Gist options
  • Save xyproto/6584125 to your computer and use it in GitHub Desktop.
Save xyproto/6584125 to your computer and use it in GitHub Desktop.

My efforts to port http://www.youtube.com/watch?v=f6kdp27TYZs to Clojure.

A boring function (6:22)

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
; ...

Slightly less boring (7:00)

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.


Launch boring in goroutine (8:13)

func main() {
	go boring("boring!")
}
(defn -main [& args]
  (go (boring "boring!")))

main now returns immediately.


Ignoring it a little less (8:19)

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.

Using channels (12:01)

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.")))

Generator: function that returns a channel (14:26)

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.")))

Channels as a handle on a service (16:24)

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.")))

Multiplexing (17:37)

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.")))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment