|
1 | 1 | ^{:kindly/hide-code true |
2 | | - :clay {:title "Core Async Flow Exploration" |
| 2 | + :clay {:title "What He Saw Before His Wings Melted" |
3 | 3 | :quarto {:author [:daslu :timothypratley] |
4 | 4 | :type :post |
5 | 5 | :draft true |
6 | 6 | :date "2025-05-1" |
7 | 7 | :category :clojure |
8 | 8 | :tags [:core.async :core.async.flow]}}} |
9 | | -(ns core.async.flow.exploration |
| 9 | +(ns core.async.flow.example.before-his-wings-melted |
10 | 10 | (:require [clojure.core.async :as async] |
11 | 11 | [clojure.core.async.flow :as flow] |
12 | 12 | [clojure.datafy :as datafy] |
13 | | - [core.async.flow.example.stats :as stats] |
14 | | - [core.async.flow.visualization :as fv])) |
| 13 | + [core.async.flow.example.asynctopolis :as asynctopolis] |
| 14 | + [core.async.flow.example.flow-show :as show])) |
15 | 15 |
|
16 | | -;; One of Clojure’s superpowers is the ability to coordinate asynchronous operations |
17 | | -;; using `core.async`. |
| 16 | +;; Long before he flew too high, |
| 17 | +;; before the wax gave way and the world remembered only his fall, |
| 18 | +;; Iccarus flew *low*. |
| 19 | +;; They often leave out this part of his misadventures, |
| 20 | +;; when curiosity, not hubris, guided his wings. |
| 21 | +;; He flew not to ascend to Olympus, |
| 22 | +;; but rather to get a good view of the lesser known Asynctopolis. |
| 23 | + |
| 24 | +;; A city pulsing with signals, stitched together by invisible threads. |
| 25 | +;; From above, its patterns unfolded like a diagram. |
| 26 | +;; Flows of information, agents in silent collaboration, |
| 27 | +;; each unaware of the others, yet perfectly aligned. |
| 28 | + |
| 29 | +;; This is what he saw. |
| 30 | + |
| 31 | +;; ## Asynctopolis from the Clouds |
| 32 | + |
| 33 | +(show/flow-svg asynctopolis/flow {:show-chans false |
| 34 | + :with-content false}) |
| 35 | + |
| 36 | +;; Coordinate asynchronous operations using `core.async`. |
18 | 37 | ;; While powerful, these operations can become hard to reason about as they grow in complexity. |
| 38 | +;; The `core.async.flow` library is a higher-level abstraction for modeling async processes as a Directed Acyclic Graph (DAG). |
| 39 | +;; We can visualize flows [flow-monitor](https://github.com/clojure/core.async.flow-monitor). |
| 40 | + |
| 41 | +;; He circled the skyline. |
| 42 | +;; He watched the channels breathe. |
| 43 | +;; And slowly, he spiraled down, |
| 44 | +;; drawn not by ambition, but fascination— |
| 45 | +;; closer to each process, |
| 46 | +;; each transformation, |
| 47 | +;; each role in the great asynchronous allegiance. |
| 48 | + |
19 | 49 |
|
20 | | -;; The new `core.async.flow` library offers a higher-level abstraction for modeling |
21 | | -;; async processes as a **Directed Acyclic Graph (DAG)**. |
22 | | -;; And now, with [flow-monitor](https://github.com/clojure/core.async.flow-monitor), |
23 | | -;; we can *visualize* and *analyze* those flows. |
24 | | -;; |
25 | 50 | ;; Let's walk through an exploration of such a flow. |
26 | 51 |
|
27 | 52 | ;; ## What We'll Explore |
|
32 | 57 | ;; 2. **Static visualization**: How can we inspect its components? |
33 | 58 | ;; 3. **Dynamic interaction**: How do values move through the flow, and what happens when they do? |
34 | 59 |
|
35 | | -;; ## 1. Creating a Flow |
36 | | - |
37 | | -;; Flows are created from configuration |
38 | | - |
39 | | -(def stats-flow |
40 | | - (flow/create-flow stats/config)) |
41 | | - |
42 | | -(fv/flow-svg stats-flow) |
43 | | - |
44 | 60 | ;; This flow models a small system involving aggregation, notification, and reporting. |
45 | 61 | ;; Internally, it consists of processes connected via channels. |
| 62 | +(show/flow-svg asynctopolis/flow {:chans-as-ports true |
| 63 | + :with-content false}) |
46 | 64 |
|
47 | | -;; ## 2. Inspecting the Flow |
| 65 | +(show/flow-svg asynctopolis/flow {:chans-as-ports false |
| 66 | + :with-content false}) |
48 | 67 |
|
49 | | -;; That's a lot to take in! Fortunately, we can make things more digestible |
50 | | -;; by viewing just the **processes** involved. |
51 | 68 |
|
52 | | -(fv/proc-table stats-flow) |
| 69 | +(show/proc-table asynctopolis/flow) |
53 | 70 |
|
54 | 71 | ;; This table gives us a clear list of components in the flow, including their names |
55 | 72 | ;; and behaviors. |
56 | 73 |
|
57 | 74 | ;; Next, let’s examine how these processes are **connected**. |
58 | 75 |
|
59 | | -(fv/conn-table stats-flow) |
| 76 | +(show/conn-table asynctopolis/flow) |
60 | 77 |
|
61 | 78 | ;; Now we’re seeing the wiring: who talks to whom, and through what channels. |
62 | 79 |
|
63 | 80 | ;; Flows implement the `Datafy` protocol so we can inspect them as data... |
64 | 81 | ;; Good luck with that, there's a lot of it |
65 | 82 |
|
66 | 83 | ^:kind/portal |
67 | | -(datafy/datafy stats-flow) |
| 84 | +(datafy/datafy asynctopolis/flow) |
68 | 85 |
|
69 | 86 |
|
70 | 87 | ;; ## 3. Running the Flow |
71 | 88 |
|
72 | 89 | ;; Time to bring our flow to life! |
73 | 90 | ;; Calling `start` activates the processes and returns a map of the important channels for interaction. |
74 | 91 |
|
75 | | -(def chs (flow/start stats-flow)) |
76 | | - |
77 | 92 | ;; We can now **inject values** into specific points in the flow. |
78 | 93 | ;; Think of this like poking the system and watching how it reacts. |
79 | 94 |
|
80 | 95 | ;; We send a “poke” signal to the `aggregator` process. |
81 | 96 |
|
82 | | -@(flow/inject stats-flow [:aggregator :poke] [true]) |
| 97 | +@(flow/inject asynctopolis/flow [:Tallystrix :poke] [true]) |
83 | 98 |
|
84 | 99 | ;; We send a stat string that is designed to trigger an alert. |
85 | 100 |
|
86 | | -@(flow/inject stats-flow [:aggregator :stat] ["abc1000"]) |
| 101 | +@(flow/inject asynctopolis/flow [:Tallystrix :stat] ["abc1000"]) |
87 | 102 |
|
88 | 103 | ;; We send a notification message into the `notifier`. |
89 | 104 |
|
90 | | -@(flow/inject stats-flow [:notifier :in] [:sandwich]) |
| 105 | +@(flow/inject asynctopolis/flow [:Claxxus :in] [:sandwich]) |
91 | 106 |
|
92 | 107 | ;; ## 4. Observing the Results |
93 | 108 |
|
94 | 109 | ;; Our flow includes a `report-chan`, where summaries and reports might be sent. |
95 | 110 |
|
96 | | -(def report-chan (:report-chan chs)) |
| 111 | +(def report-chan (:report-chan asynctopolis/chs)) |
97 | 112 |
|
98 | | -(flow/ping stats-flow) |
| 113 | +(flow/ping asynctopolis/flow) |
99 | 114 |
|
100 | 115 | (async/poll! report-chan) |
101 | 116 |
|
102 | 117 | ;; After pinging the system, we check if anything landed in the report channel. |
103 | 118 |
|
104 | 119 | ;; We can also inspect the `error-chan`, where any issues in the flow are reported. |
105 | 120 |
|
106 | | -(def error-chan (:error-chan chs)) |
| 121 | +(def error-chan (:error-chan asynctopolis/chs)) |
107 | 122 |
|
108 | 123 | (async/poll! error-chan) |
109 | 124 |
|
|
0 commit comments