Selecting & Sending
The built-in String.Builder
type is defined this way:
type String.Builder = iterative choice {
.add(String) => self,
.build => String,
}
It’s basically an object, in an OOP-fashion, with two methods: .add
and .build
. At the top
level, it’s an iterative choice: an object that
can be repeatedly interacted with.
We construct an empty String.Builder
using the built-in definition of the same name:
def LetsBuildStrings = do {
let builder = String.Builder
Selection
Now, we have a local variable builder
of the type String.Builder
.
When learning about iterative types, we learned that we can treat them
as their underlying body. For String.Builder
, it’s a choice type, and the command for those is
the selection command.
All commands start with their subject. Here it’s the builder
variable. The selection command
itself then looks the same as the usual destruction of a choice.
def LetsBuildStrings = do {
let builder = String.Builder
builder.add // selection command
That’s it! Now, here’s the crucial bit: after selection, builder
changes its type to the type
of the selected branch. Here’s the one we selected:
.add(String) => self,
The argument on the left side of =>
is just a syntax sugar for a function.
De-sugared, it is:
.add => [String] self,
Therefore, the type of builder
after this builder.add
command becomes a function:
builder: [String] iterative choice {
.add(String) => self,
.build => String,
}
The
self
in the original branch got replaced by its correspondingiterative
— in this case, the originalString.Builder
.
Sending
For a function type, we have the send command. It’s just like a function call — but as a command, it doesn’t have a result. Instead, it turns the subject itself to the result.
def LetsBuildStrings = do {
let builder = String.Builder
builder.add // selection command
builder("Hello") // send command
You may have noticed, that the selection and send commands behave the same as a combination of a regular destruction and re-assignment of the variable. In code:
let builder = builder.add let builder = builder("Hello")
For these two, the behavior matches perfectly! It’s a good way to build intuition about what these commands mean. However, this simple translation stops working as we get into the
.case
and receive commands.
After sending the string, builder
turns back into the original String.Builder
, so we can
keep adding more content to it.
def LetsBuildStrings = do {
let builder = String.Builder
builder.add // selection command
builder("Hello") // send command
builder.add // selection command
builder(", ") // send command
builder.add // selection command
builder("World") // send command
builder.add // selection command
builder("!") // send command
Chaining commands
This is rather noisy, but we can improve it! Multiple consecutive commands on the same subject, can be chained together, without repeating the subject.
def LetsBuildStrings = do {
let builder = String.Builder
builder.add("Hello")
builder.add(", ")
builder.add("World")
builder.add("!")
Or even:
def LetsBuildStrings = do {
let builder = String.Builder
builder
.add("Hello")
.add(", ")
.add("World")
.add("!")
I like the first variant better, though.
To complete the do
expression, let’s just return the constructed string:
def LetsBuildStrings = do {
let builder = String.Builder
builder.add("Hello")
builder.add(", ")
builder.add("World")
builder.add("!")
} in builder.build // = "Hello, world!"