A distributed system with a native GUI in a tweet

My recent silence hasn’t been due to a lack of progress, but rather an abundance of it. I’ve been deeply immersed in the development of Objective-S, and the rapid advancements have left me with a sense of awe and a need to pause and reflect.

To illustrate this progress, I’d like to present a distributed system, complete with a GUI, concisely expressed in a single tweet:

#!env stui
scheme:s3 ← ref:http://defiant.local:2345/ asScheme
text ← #NSTextField{ #stringValue:’’,#frame:(10@45 extent:180@24) }.
window ← #NSWindow{ #frame:(300@300 extent:200@105),#title:‘S3’, #views:#[text]}.
text → ref:s3:bucket1/msg.txt.
app runFromCLI:window.

— Marcel Weiher 🇪🇺 (@mpweiher) August 9, 2022

This script generates a window featuring a text field. Any text entered into this field is directly saved to an S3 bucket. This process continues until the user closes the window, at which point the program ends.

Admittedly, this is a simplified illustration of a distributed system, especially since the code for the S3 simulator is not included.

However, despite its conciseness, the Objective-S script is not as simplistic as it might appear to those unfamiliar with the language. code golf.

Instead, it elegantly defines and connects the necessary elements:

  1. A storage combinator to interact with data stored in S3.
  2. A text field within a window, both defined as object literals.
  3. A connection that links the text field to a specific S3 bucket.

The elegance lies in the fact that the code’s structure directly mirrors the structure of the system itself. Let’s break down these components further.

Interacting with S3 using a Storage Combinator

The initial line of the script establishes an S3 scheme handler. This handler enables interaction with S3 buckets in a manner similar to working with local variables. To illustrate, this line of code saves the text “Hello World!” to a file named “msg.txt” within a bucket called “bucket1”:

1
2
 s3:bucket1/msg.txt ← 'Hello World!'
```Retrieving the stored text is just as straightforward:

stdout println: s3:bucket1/msg.txt ```Our S3 simulator is accessible at http://defiant.local:2345/, hosted on a machine named “defiant” on the local network. This machine is discoverable via Bonjour and listens on port 2345. Thanks to Objective-S’s support for Polymorphic Identifiers (pdf), this URL itself can be used as a directly evaluable identifier within the language. However, this directness presents a challenge. In most programming languages, including Objective-S, using an identifier directly retrieves the value associated with the variable it represents. In the case of http://defiant.local:2345/, this would result in the directory listing of the S3 server’s root, returned as XML:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
 `123FakeS3
 bucket1
2022-08-10T15:18:32.000Z` 
```Our aim is not to retrieve the directory listing, but to reference the URL itself. This is where `ref:` proves valuable. It prevents immediate evaluation, instead returning the reference, akin to using the `&` operator to create pointers in C.

However, an Objective-S reference (specifically, a _binding_) is significantly more powerful than a C pointer. Notably, sending the `-asScheme` message to a reference transforms it into a store. This store then uses the original reference as its base URL, ensuring that all subsequent references it receives are evaluated relative to this base.

In essence, defining the `s3:` scheme handler as described means that the expression `s3:bucket1/msg.txt` evaluates to `http://defiant.local:2345/bucket1/msg.txt`.

This method of creating shorthand references has proven invaluable in simplifying complex references and promoting modularity, making it a prevalent pattern in Objective-S code.

### Building GUIs Declaratively with Object Literals

Next, we define the GUI: a window containing a text field. Object literals make this process incredibly concise. Similar to dictionary literals, object literals use key/value pairs to define an instance. However, they offer the flexibility to specify the class of the instance being created, moving beyond the limitations of dictionaries.

The following illustrates how a text field with specific dimensions can be defined and assigned to the local variable `text`:

text ← #NSTextField{ #stringValue:’’,#frame:(10@45 extent:180@24) }. Similarly, a window containing the previously defined text field can be created: ` window ← #NSWindow{ #frame:(300@300 extent:200@105),#title:‘S3’, #views:#[text]}. ``` `While it would be ideal to define the text field directly within the window definition, a separate variable is currently necessary to facilitate the connection step, which we’ll cover next.

Establishing Connections Between Components

Having established the text field (within a window) and the storage destination, we need to connect them. Traditionally, this would involve procedures, callbacks, or external mechanisms to establish or define this connection. In Objective-S, however, we connect components directly:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
 text → ref:s3:bucket1/msg.txt.
```That's all it takes.

The right arrow "→" acts as a polymorphic connection "operator." The underlying connection mechanism is far more intricate than this simple representation:

1.  It starts from a specific port on the source component.
2.  It connects to a role within the mediating connector that's compatible with that source port.
3.  It then connects to a role within the connector that's compatible with the target object's port.
4.  Finally, it connects to the corresponding compatible port on the target component.

While each of these steps can be explicitly specified, the underlying machinery can often infer compatible ports and roles, simplifying the process. In this scenario, even the specific connector was automatically determined.

Instead of a remote S3 bucket, we could easily store data in a local file:

text → ref:file:/tmp/msg.txt.

text → ref:var:message.

1
2

Alternatively, we can append individual messages to a stream, such as `stdout`:

text → stdout. This would print the text field's content to the console every time the user presses Enter. Or, we can append to a file by connecting to the file's associated stream: ` text → ref:file:/tmp/msg.txt outputStream. ``` `This is not limited to single stream sinks; it can be extended to complex processing pipelines.

The key takeaway is that this is not a case of achieving compact code through overly specialized components and mechanisms (a common approach in low-code/no-code solutions that often falters beyond simple demos).

Instead, this represents a novel approach to component creation, interface definition, and component connection in an exceptionally straightforward way. gluing

Comparing Eval/Apply and Connect/Run Paradigms

With our system constructed by configuring and connecting components, the final step is to execute it. CLIApp is a specialized subclass of NSApplication designed to run without the typical app wrapper or Info.plist file. The stui script runner instantiates it before script execution, making the instance accessible via the app variable within the script.

This is where we transition from our connected component paradigm back to the familiar world of call/return, mirroring how Cocoa’s auto-generated main function interacts with NSApplicationMain().

The distinction between eval/apply (call/return) and connect/run is significant and warrants a dedicated discussion.

It’s important to note that call/return mechanisms are not entirely absent. They remain valuable for specific tasks, like transforming an element. However, for system construction, defining, configuring, and connecting components directly (“declaratively”) proves far superior to procedural approaches, including those using recently popular “fluent APIs” often mislabeled as declarative.

This project continues to exceed my expectations, and I’m incredibly enthusiastic about its potential.

Licensed under CC BY-NC-SA 4.0
Last updated on Mar 26, 2024 02:05 +0100