Strings

Beamtalk strings are UTF-8 binaries. String operations are grapheme-aware: size counts visible characters (grapheme clusters), not bytes.

String literals use double quotes. Single quotes are NOT string delimiters.

String literals

"Hello, World!"                  // => Hello, World!
"Unicode: café, 日本語, 🌍"        // => Unicode: café, 日本語, 🌍
"" isEmpty                       // => true

A literal double-quote inside a string — double it:

"She said ""hello"""  // => She said "hello"

String interpolation

Embed any expression inside {} to interpolate it. The expression is evaluated and converted to a string via printString.

name := "Alice"          // => _
"Hello, {name}!"         // => Hello, Alice!

x := 42                  // => _
"The answer is {x}."     // => The answer is 42.

Expressions, not just variables:

"2 + 2 = {2 + 2}"  // => 2 + 2 = 4

Nested quotes work inside {}:

items := #[1, 2, 3]           // => _
"Items: {items size} total"    // => Items: 3 total

Escape { with \{ to suppress interpolation:

"Literal: \{name\}"  // => Literal: {name}

Size and indexing

size / length count grapheme clusters (visible characters):

"hello" size  // => 5
"café" size   // => 4
"🌍" size     // => 1

Access individual characters (1-based indexing):

"hello" at: 1  // => h
"hello" at: 5  // => o
"café" at: 4   // => é

Concatenation and building strings

"Hello" ++ ", " ++ "World!"  // => Hello, World!

Join a list of strings with a separator:

#("a", "b", "c") join: ", "        // => a, b, c
#("Hello", " ", "World") join: ""  // => Hello World

Repeat a string using *:

"ab" * 3  // => ababab

Searching and testing

"hello world" includesSubstring: "world"  // => true
"hello world" includesSubstring: "xyz"    // => false
"hello" startsWith: "hel"                 // => true
"hello" endsWith: "llo"                   // => true
"hello" indexOf: "l"                      // => 3
"" isEmpty                                // => true
"hello" isEmpty                           // => false
"hello" isNotEmpty                        // => true

Case conversion

"hello" uppercase  // => HELLO
"HELLO" lowercase  // => hello

Unicode-aware (German sharp-s):

"straße" uppercase  // => STRASSE

Trimming

"  hello  " trim  // => hello

Splitting

"a,b,c" split: ","           // => ["a","b","c"]
"hello world foo" split: " "  // => ["hello","world","foo"]

Split on any whitespace:

"hello   world" words  // => ["hello","world"]

Replacing

"hello world" replaceAll: "world" with: "Beamtalk"  // => hello Beamtalk

Conversion

String to number:

"42" asInteger   // => 42
"3.14" asFloat   // => 3.14

Number to string:

42 printString    // => 42
3.14 printString  // => 3.14

Symbol to string:

#hello asString   // => hello

Character access

Collect applies a block to each character and joins the results back into a string:

"hello" collect: [:ch | ch uppercase]  // => HELLO

Multiline strings

There is no multi-line string literal syntax. Use ++ to concatenate lines:

multiline := "line 1" ++ "\n" ++ "line 2" ++ "\n" ++ "line 3"  // => _
multiline size  // => 22

Summary

Exercises

1. Self-describing string. Write an expression that produces "hello has 5 characters" using string interpolation and the size message.

Hint
word := "hello"
"{word} has {word size} characters"

2. Clean and normalize. Take the string " Hello, World! " and produce "hello, world!" — trimmed and lowercased — in a single expression chain.

Hint

" Hello, World! " trim lowercase — unary messages chain left-to-right.

3. Split, reverse, rejoin. Split "one:two:three" on ":", reverse the resulting array, and join it back with "-". What string do you get?

Hint
("one:two:three" split: ":") reversed join: "-"

Result: "three-two-one".

Next: Chapter 7 — Blocks