Some months ago (anno 2023~2024), when thinking about how to implement communication between UI elements, I thought up a concept that turned out to be pretty flexible.
When exploring the idea more, I figured that it's not only relevant for communication between UI elements, but can be used for communication between the UI elements and the UI server, and ultimately could be useful in all sorts of distributed systems.
The idea is rather simple, I'm guessing someone else came up with it before me. But I haven't found it anywhere yet.
And maybe it's not as useful as I think it is - but I think it is.
Don't communicate by sending messages, communicate by sending channels... and messages... containing channels.
Usually, systems communicate with each other using some kind of encoding. Duh, most networks use strings of bytes. To get information over those networks, you need to put your fancy pointers and structs and strings and booleans and files and unions and associative arrays and Animals and IEEE754s and watchamacallits in a format that the network can handle. One byte. After another.
Basically, you're sending a string that may represent lots of other things.
Different encodings support different types, unsupported types may still be transferable (although maybe not recommended) by adding layers.
E.g. in JSON, null, booleans, numbers, strings, string associative arrays, arrays, are first-class values.
If you want to encode a Date
,
you might need to use a string
(ISO8601)
or a number
(Unix Time)
and add a second layer that converts Date
types.
Go has channels as first-class values.
Can encoding formats have channels as first-class values?
For my UI use case, primarily, I did not need communication between two systems.
I needed one system to specify how another communicates within itself.
E.g.
+-------+------+
| input | view |
+-------+------+
| input | view |
+-------+------+
I want the top input to communicate with the bottom view. And the bottom input to communicate with the top view.
I want to parameterize the elements with channels.
ch1 = makeChan()
ch2 = makeChan()
+------------+-----------+
| input(ch1) | view(ch2) |
+------------+-----------+
| input(ch2) | view(ch1) |
+------------+-----------+
But how can I do that when the structure is defined on one system and another is executing it?
I can use matching channel names which I can send over the network.
+------------+-----------+
| input("1") | view("2") |
+------------+-----------+
| input("2") | view("1") |
+------------+-----------+
When wanting to communicate over a channel, the UI elements initially go to a central "channel registry" and convert their channel names to actual channels. Elements asking for equal channel names will receive equal channels and can communicate over them.
[
[{"type": "input", "change": ("1")}, {"type": "view", "show": ("2")}],
[{"type": "input", "change": ("2")}, {"type": "view", "show": ("1")}]
]
Now that we can encode channels it becomes easy for the server to involve itself in the running system on the client.
It just sends a channel, and some data that should be sent to the channel.
{
"type": "telegram",
"value": "hello from the server",
"channel": ("2"),
}
Note that the value is not restricted to being a string. It can be anything the format allows - including channels.
If you want to use telegrams, you need some kind of "telegram executor" on receiving systems. All it does is:
for tg := range telegrams {
tg.channel <- tg.value
}
If multiple systems have channel registries and telegram executors, they can send telegrams to each other.
Channels can be connected to channels on other systems by using programs that receive from a channel, then send telegrams to other systems.
Gob Encoding missed out by not allowing channels. The channel might not make sense to the reader of the gob stream. But as soon as the channel finds its way back to the running system that created it, it can be used.
I'm not saying Gob Encoding should have channel support. It would introduce unwanted complexity. But it would've been interesting!
By adding a channel type to encoding formats you can communicate with another system on a granular level.
Usually, granular communication requires complicated routing. With this, complicated routing techniques can be implemented when needed, but they are not required for basic communication.
Even though, a channel only makes sense on a single system, by using "telegrams", you can send values to channels on other systems.
The added complexities are: