What He Saw Before His Wings Melted

Published

May 1, 2025

Long before he flew too high, before the wax gave way and the world remembered only his fall, Icarus flew low. They often leave out this part of his misadventures, when curiosity, not hubris, guided his wings. He flew not to ascend to Olympus, but rather to get a good view of the lesser known Asynctopolis.

A city pulsing with signals, stitched together by invisible threads. From above, its patterns unfolded like a diagram. Flows of information, agents in silent collaboration, each unaware of the others, yet perfectly aligned.

This is what he saw.

Asynctopolis from the Clouds

(def asynctopolis (flow/create-flow asynctopolis/config))
(show/flow-svg asynctopolis nil {:show-chans   false
                                 :with-content false})
Tallystrix
Chronon
Claxxus
Randomius

He circled the skyline. He watched the channels breathe. And slowly, he spiraled down, drawn not by ambition, but fascination— closer to each process, each transformation, each role in the great asynchronous allegiance.

As he flew lower he saw that processes are connected via channels.

(show/flow-svg asynctopolis nil {:chans-as-ports true
                                 :with-content   false})
Tallystrix
stat
poke
alert
Chronon
out
Claxxus
in
Randomius
out

Are channels attached to a process, or are they part of it? You can choose to visualize them as distinct connectors, or as embedded roles within each process. Both perspectives reveal useful insights.

(show/flow-svg asynctopolis nil {:chans-as-ports false
                                 :with-content   false})
Tallystrix
stat
poke
alert
Chronon
out
Claxxus
in
Randomius
out

Wanting to see more, Icarus swooped even lower to survey the processes.

(show/proc-table asynctopolis)
process start params in chans out chans
Tallystrix
min: 1
Min value, alert if lower
max: 10
Max value, alert if higher
stat: Channel to receive stat values
poke: Channel to poke when it is time to report a window of data to the log
alert: Notify of value out of range {:val value, :error :high|:low
Chronon
wait: 3000
Time to wait between pokes
out: Poke channel, will send true when the alarm goes off
Claxxus
prefix: Alert:
Log message prefix
in: Channel to receive messages
Randomius
min: 0
Min value to generate
max: 12
Max value to generate
wait: 500
Time in ms to wait between generating
out: Output channel for stats

With a clearer understanding of the processes, he pondered how these processes are connected.

(show/conn-table asynctopolis)
source target
Randomius_out Tallystrix_stat
Chronon_out Tallystrix_poke
Tallystrix_alert Claxxus_in

In doing so he realized there are also 2 global channels, report and error:

(show/flow-svg asynctopolis nil {:chans-as-ports    false
                                 :with-content      false
                                 :show-global-chans true})
Tallystrix
stat
poke
alert
Chronon
out
Claxxus
in
Randomius
out
report
error

Any process can put messages on report and error.

Street Level

Reaching street level, he called out start! The flow responded, handing him report and error channels in a map.

(def chs (flow/start asynctopolis))
OUT
Randomius initialing
Tallystrix initializing
Chronon initializing
Claxxus initializing

But still, nothing stirred. So he yelled resume!

(flow/resume asynctopolis)
true
(def report-chan (:report-chan chs))
(async/poll! report-chan)
nil
(flow/inject asynctopolis [:Tallystrix :poke] [true])
#object [FutureTask]
(flow/inject asynctopolis [:Tallystrix :stat] ["abc1000"])
THREAD OUT
Claxxus transitioning :clojure.core.async.flow/resume
Tallystrix transitioning :clojure.core.async.flow/resume
Chronon transitioning :clojure.core.async.flow/resume
Chronon running
Randomius transitioning :clojure.core.async.flow/resume
Talon, set flight!
#object [FutureTask]
(show/flow-svg asynctopolis chs {:chans-as-ports false
                                 :with-content   false})
THREAD OUT
Chronon transforms :alarm true to :out
SQUARK: 9
Tallystrix transforming :poke true
Randomius transform 9 from :stat to :out
Tallystrix transforming :stat abc1000
Tallystrix transforming :stat 9
Tallystrix transforming :poke true
Tallystrix
stat
poke
alert
Chronon
out
Claxxus
in
Randomius
out

Tallystrix takes only numbers, "abc1000" was not acceptable.

Conclusion

(flow/stop asynctopolis)
THREAD OUT
Tallystrix transitioning :clojure.core.async.flow/stop
Randomius transitioning :clojure.core.async.flow/stop
Talon, rest!
true
(Thread/sleep 1)
THREAD OUT
Claxxus transitioning :clojure.core.async.flow/stop
Chronon transitioning :clojure.core.async.flow/stop
Chronon rests.
nil

Icarus realized that Flow is a library for building concurrent, event-driven systems out of simple, communication-free functions. Processes connect through channels. You define the structure as a directed graph. Flow takes care of orchestration. Flows are data-driven, easy to inspect, reason about and visualize. Then he wondered just how high could he fly?

Happy flowing, and keep your feathers waxed!

source: src/core/async/flow/example/before_his_wings_melted.clj