essenceDocumentation
essenceDocs
on this pageAt a glance
Coming from JavaScript?

String

type

Text, measured in characters: searching, cutting, building, changing case, and comparing.

Taught on: Programs and values and Strings.

At a glance

Declared
namespace String for String
Refinements
Modes
  • SideWhich end of a String a Method works on: the start, the end, or both ends.
  • CaseSensitivityWhether a String comparison treats upper and lower case as the same.
  • NormalizationFormWhich Unicode normalization form normalize(as:) produces.

Creating

of

on String

Builds a String out of Unicode code points.

A point that names no character answers nothing. A surrogate names none, and neither does a point past the last one Unicode gives.

A point known while the Program is being written is a \u{…} escape instead. The escape character is written "\u{1B}", and answers a String rather than an Optional<String>. These entries are for a point worked out while the Program runs.

static of(codePoint code: NonNegativeInteger) -> Optional<String>
NameTypeDescription
codePointNonNegativeIntegerthe code point to read, proven not to be negative

the String of that one character, or nothing when the point names none.

Example
expect String.of(codePoint 97)::is("a")
expect String.of(codePoint 55296)::isEmpty()
static of(codePoints codes: List<Integer>) -> Optional<String>

Builds a String out of a List of Unicode code points.

One point that names no character refuses the whole List. A String missing the characters it could not read is a String no caller asked for. An empty List answers the empty String.

NameTypeDescription
codePointsList<Integer>the code points to read

the String of those characters, or nothing when a point names none.

Example
expect String.of(codePoints [104, 105])::is("hi")
expect String.of(codePoints [104, -1])::isEmpty()

Equality, ordering and printing

is

on String

Answers whether the String has the same characters as another one.

The comparison is case-sensitive unless a CaseSensitivity says otherwise.

is(_ other: String) -> Boolean
NameTypeDescription
_Stringthe String to compare against

true when the Strings are equal.

Example
expect "Ada"::is("Ada")
expect "Ada"::is("ada")::negate()
is(
	_ other: String,
	comparing sensitivity: CaseSensitivity,
) -> Boolean
NameTypeDescription
_Stringthe String to compare against
comparingCaseSensitivitywhether case is significant

true when the Strings are equal under the given CaseSensitivity.

Example
expect "Ada"::is("ADA", comparing #Insensitive)
expect "Ada"::is("ADA", comparing #Sensitive)::negate()

compare

on String

Orders the String against another one, by character code point.

A CaseSensitivity of #Insensitive folds the case first.

compare(to other: String) -> Ordering
NameTypeDescription
toStringthe String to order against

Ordering#Less, Ordering#Equal or Ordering#Greater.

Example
expect "ant"::compare(to "bee")::is(#Less)
expect "Zoo"::compare(to "ant")::is(#Less)
compare(
	to other: String,
	comparing sensitivity: CaseSensitivity,
) -> Ordering
NameTypeDescription
toStringthe String to order against
comparingCaseSensitivitywhether case is significant

Ordering#Less, Ordering#Equal or Ordering#Greater.

Example
expect "Zoo"::compare(to "ant", comparing #Insensitive)::is(#Greater)
expect "ANT"::compare(to "ant", comparing #Insensitive)::is(#Equal)

toString

on String

toString() -> String

Answers the String itself.

the String, unchanged.

Example
expect "Ada"::toString()::is("Ada")

isNot

on String · provided by Equatable

isNot(_ other: String) -> Boolean

Answers whether the values differ.

NameTypeDescription
_Stringthe value to compare with

true when the values differ.

Example
expect "Ada"::isNot("Grace")
expect 3::isNot(3)::negate()

Comparing

isLessThan

on String · provided by Comparable

isLessThan(_ other: String) -> Boolean

Answers whether the value is strictly below another.

NameTypeDescription
_Stringthe value to compare with

true when the value is below the given one.

Example
expect 3::isLessThan(5)
expect 5::isLessThan(5)::negate()

isLessThanOrEqualTo

on String · provided by Comparable

isLessThanOrEqualTo(_ other: String) -> Boolean

Answers whether the value is below another, or equal to it.

NameTypeDescription
_Stringthe value to compare with

true when the value is below the given one or equal to it.

Example
expect 3::isLessThanOrEqualTo(5)
expect 5::isLessThanOrEqualTo(5)

isGreaterThan

on String · provided by Comparable

isGreaterThan(_ other: String) -> Boolean

Answers whether the value is strictly above another.

NameTypeDescription
_Stringthe value to compare with

true when the value is above the given one.

Example
expect "pear"::isGreaterThan("apple")
expect "pear"::isGreaterThan("pear")::negate()

isGreaterThanOrEqualTo

on String · provided by Comparable

isGreaterThanOrEqualTo(_ other: String) -> Boolean

Answers whether the value is above another, or equal to it.

NameTypeDescription
_Stringthe value to compare with

true when the value is above the given one or equal to it.

Example
expect 5::isGreaterThanOrEqualTo(3)
expect 5::isGreaterThanOrEqualTo(5)

Testing content

isEmpty

on String

isEmpty() -> Boolean

Answers whether the String has no characters.

true for the empty String, and false otherwise.

Example
expect ""::isEmpty()
expect " "::isEmpty()::negate()

hasCharacters

on String

hasCharacters() -> Boolean

Answers whether the String has at least one character.

true when the String is not empty.

Example
expect "Ada"::hasCharacters()
expect ""::hasCharacters()::negate()

isOneCharacter

on String

isOneCharacter() -> Boolean

Answers whether the String is one character long.

This is the question the Character Type is written on. A String proven to answer true here goes wherever a Character is wanted.

true when the String holds one character.

Example
expect "a"::isOneCharacter()
expect "ab"::isOneCharacter()::negate()

hasOnlyDigits

on String

hasOnlyDigits() -> Boolean

Answers whether every character of the String is a decimal digit.

A digit is any Unicode decimal digit, which is wider than what Integer.parse reads: Integer.parse reads the notation the language writes. The empty String answers true, because nothing in it is not a digit.

true when the String holds decimal digits alone.

Example
expect "2026"::hasOnlyDigits()
expect "2026-09"::hasOnlyDigits()::negate()

hasOnlyLetters

on String

hasOnlyLetters() -> Boolean

Answers whether every character of the String is a letter.

A combining mark counts as part of the letter before it. A mark standing on its own belongs to no letter and answers false. The empty String answers true.

true when the String holds letters alone.

Example
expect "Grüße"::hasOnlyLetters()
expect "Rule 34"::hasOnlyLetters()::negate()

hasOnlyLettersOrDigits

on String

hasOnlyLettersOrDigits() -> Boolean

Answers whether every character of the String is a letter or a decimal digit.

A combining mark counts as part of the character before it. The empty String answers true.

true when the String holds letters and digits alone.

Example
expect "route66"::hasOnlyLettersOrDigits()
expect "route 66"::hasOnlyLettersOrDigits()::negate()

hasOnlyWhitespace

on String

hasOnlyWhitespace() -> Boolean

Answers whether every character of the String is whitespace.

Whitespace is what Unicode calls whitespace, which is the rule trim and words read. The empty String answers true.

true when the String holds whitespace alone.

Example
expect "   "::hasOnlyWhitespace()
expect " x "::hasOnlyWhitespace()::negate()

Searching

contains

on String

Answers whether the given String occurs anywhere in this one.

The empty String occurs in every String. The search is case-sensitive unless a CaseSensitivity says otherwise.

Example
expect "Lions"::contains("ion")
expect "Lions"::doesNotContain("Tiger")
expect "Lions"::contains("ION", comparing #Insensitive)
contains(_ other: String) -> Boolean
NameTypeDescription
_Stringthe String to look for

true when it occurs.

contains(
	_ other: String,
	comparing sensitivity: CaseSensitivity,
) -> Boolean
NameTypeDescription
_Stringthe String to look for
comparingCaseSensitivitywhether case is significant

true when it occurs under the given CaseSensitivity.

doesNotContain

on String

Answers whether the given String occurs nowhere in this one.

doesNotContain(_ other: String) -> Boolean
NameTypeDescription
_Stringthe String to look for

true when it does not occur.

Example
expect "Lions"::doesNotContain("Tiger")
expect "Lions"::doesNotContain("ion")::negate()
doesNotContain(
	_ other: String,
	comparing sensitivity: CaseSensitivity,
) -> Boolean
NameTypeDescription
_Stringthe String to look for
comparingCaseSensitivitywhether case is significant

true when it does not occur under the given CaseSensitivity.

Example
expect "Lions"::doesNotContain("ION", comparing #Sensitive)
expect "Lions"::doesNotContain("ION", comparing #Insensitive)::negate()

starts

on String

Answers whether the String begins with the given one.

Example
expect "Lions"::starts(with "Li")
expect "Lions"::doesNotStart(with "Ti")
expect "Lions"::starts(with "li", comparing #Insensitive)
starts(with prefix: String) -> Boolean
NameTypeDescription
withStringthe String to look for at the front

true when the String begins with it.

starts(
	with prefix: String,
	comparing sensitivity: CaseSensitivity,
) -> Boolean
NameTypeDescription
withStringthe String to look for at the front
comparingCaseSensitivitywhether case is significant

true when the String begins with it under the given CaseSensitivity.

doesNotStart

on String

Answers whether the String does not begin with the given one.

doesNotStart(with prefix: String) -> Boolean
NameTypeDescription
withStringthe String to look for at the front

true when the String does not begin with it.

Example
expect "Lions"::doesNotStart(with "Ti")
expect "Lions"::doesNotStart(with "Li")::negate()
doesNotStart(
	with prefix: String,
	comparing sensitivity: CaseSensitivity,
) -> Boolean
NameTypeDescription
withStringthe String to look for at the front
comparingCaseSensitivitywhether case is significant

true when the String does not begin with it under the given CaseSensitivity.

Example
expect "Lions"::doesNotStart(with "li", comparing #Sensitive)
expect "Lions"::doesNotStart(with "li", comparing #Insensitive)::negate()

ends

on String

Answers whether the String ends with the given one.

ends(with suffix: String) -> Boolean
NameTypeDescription
withStringthe String to look for at the end

true when the String ends with it.

Example
expect "Lions"::ends(with "ns")
expect "Lions"::ends(with "Li")::negate()
ends(
	with suffix: String,
	comparing sensitivity: CaseSensitivity,
) -> Boolean
NameTypeDescription
withStringthe String to look for at the end
comparingCaseSensitivitywhether case is significant

true when the String ends with it under the given CaseSensitivity.

Example
expect "Lions"::ends(with "NS", comparing #Insensitive)
expect "Lions"::ends(with "NS", comparing #Sensitive)::negate()

doesNotEnd

on String

Answers whether the String does not end with the given one.

doesNotEnd(with suffix: String) -> Boolean
NameTypeDescription
withStringthe String to look for at the end

true when the String does not end with it.

Example
expect "Lions"::doesNotEnd(with "Li")
expect "Lions"::doesNotEnd(with "ns")::negate()
doesNotEnd(
	with suffix: String,
	comparing sensitivity: CaseSensitivity,
) -> Boolean
NameTypeDescription
withStringthe String to look for at the end
comparingCaseSensitivitywhether case is significant

true when the String does not end with it under the given CaseSensitivity.

Example
expect "Lions"::doesNotEnd(with "NS", comparing #Sensitive)
expect "Lions"::doesNotEnd(with "NS", comparing #Insensitive)::negate()

firstIndex

on String

Answers the position of the first occurrence of the given String.

The empty String occurs at position 0. A String that does not occur answers nothing, and the defaultingTo: entry answers the given position instead.

firstIndex(of part: String) -> Optional<Integer>
NameTypeDescription
ofStringthe String to look for

the zero-based position, or nothing when it does not occur.

Example
expect "banana"::firstIndex(of "an")::is(1)
expect "banana"::firstIndex(of "")::is(0)
expect "banana"::firstIndex(of "x")::isEmpty()
firstIndex(of part: String, defaultingTo fallback: Integer) -> Integer
NameTypeDescription
ofStringthe String to look for
defaultingToIntegerthe position to answer with when the String does not occur

the zero-based position, or the given one in its place.

Example
expect "banana"::firstIndex(of "n", defaultingTo -1)::is(2)
expect "banana"::firstIndex(of "x", defaultingTo -1)::is(-1)
firstIndex(
	of part: String,
	comparing sensitivity: CaseSensitivity,
) -> Optional<Integer>
NameTypeDescription
ofStringthe String to look for
comparingCaseSensitivitywhether case is significant

the zero-based position, or nothing when it does not occur under the given CaseSensitivity.

Example
expect "Anna"::firstIndex(of "a", comparing #Insensitive)::is(0)
expect "Anna"::firstIndex(of "a", comparing #Sensitive)::is(3)

lastIndex

on String

Answers the position of the last occurrence of the given String.

The occurrence can overlap an earlier one: "aaa"::lastIndex(of "aa") is 1. The empty String occurs at the length. A String that does not occur answers nothing, and the defaultingTo: entry answers the given position instead.

lastIndex(of part: String) -> Optional<Integer>
NameTypeDescription
ofStringthe String to look for

the zero-based position, or nothing when it does not occur.

Example
expect "banana"::lastIndex(of "an")::is(3)
expect "aaa"::lastIndex(of "aa")::is(1)
expect "banana"::lastIndex(of "x")::isEmpty()
lastIndex(of part: String, defaultingTo fallback: Integer) -> Integer
NameTypeDescription
ofStringthe String to look for
defaultingToIntegerthe position to answer with when the String does not occur

the zero-based position, or the given one in its place.

Example
expect "banana"::lastIndex(of "n", defaultingTo -1)::is(4)
expect "banana"::lastIndex(of "x", defaultingTo -1)::is(-1)
lastIndex(
	of part: String,
	comparing sensitivity: CaseSensitivity,
) -> Optional<Integer>
NameTypeDescription
ofStringthe String to look for
comparingCaseSensitivitywhether case is significant

the zero-based position, or nothing when it does not occur under the given CaseSensitivity.

Example
expect "Anna"::lastIndex(of "A", comparing #Insensitive)::is(3)
expect "Anna"::lastIndex(of "A", comparing #Sensitive)::is(0)

everyIndex

on String

everyIndex(of part: String) -> List<Integer>

Answers the position of every occurrence of the given String.

The occurrences do not overlap, which is the rule count(of:) counts by. The empty String occurs at no position.

NameTypeDescription
ofStringthe String to look for

the List of zero-based positions, in the order they occur.

Example
expect "banana"::everyIndex(of "an")::is([1, 3])

count

on String

Answers how many times the given String occurs in this one.

The occurrences do not overlap: "aaa"::count(of "aa") is 1. The empty String occurs 0 times.

count(of part: String) -> NonNegativeInteger
NameTypeDescription
ofStringthe String to count

the number of occurrences, which is never negative.

Example
expect "banana"::count(of "a")::is(3)
expect "aaa"::count(of "aa")::is(1)
expect "banana"::count(of "")::is(0)
count(
	of part: String,
	comparing sensitivity: CaseSensitivity,
) -> NonNegativeInteger
NameTypeDescription
ofStringthe String to count
comparingCaseSensitivitywhether case is significant

the number of occurrences under the given CaseSensitivity, which is never negative.

Example
expect "Anna"::count(of "a", comparing #Insensitive)::is(2)
expect "Anna"::count(of "a", comparing #Sensitive)::is(1)

Reading

lines

on String

lines() -> NonEmptyList<String>

Answers the lines of the String, split at every line break.

A line break is \n, \r or \r\n. A trailing break leaves a final empty line. The empty String is one empty line.

the List of lines, without the line breaks. The List always has a line in it.

Example
expect "one\ntwo"::lines()::is(["one", "two"])
expect "one\r\ntwo\n"::lines()::is(["one", "two", ""])
expect ""::lines()::is([""])

words

on String

words() -> List<String>

Answers the words of the String.

A word is a run of characters that are not whitespace. The whitespace between the words is dropped.

the List of words, empty when the String is only whitespace.

Example
expect " the  quick fox "::words()::is(["the", "quick", "fox"])
expect "   "::words()::isEmpty()

length

on String

Answers how many characters the String has.

the number of characters, which is never negative.

Example
expect "Grüße"::length()::is(5)
expect ""::length()::is(0)

on NonEmptyString

length() -> PositiveInteger

Answers how many characters the String has, which is at least one.

the number of characters, which is above zero.

Example
constant size: PositiveInteger = "Ada"::length()

expect size::is(3)

characters

on String

characters() -> List<Character>

Answers the characters of the String, each as its own String.

the List of characters.

Example
expect "Ada"::characters()::is(["A", "d", "a"])
expect ""::characters()::isEmpty()

on NonEmptyString

characters() -> NonEmptyList<Character>

Answers the characters of the String, each as its own String.

the List of characters, which always holds at least one.

Example
constant letters: NonEmptyList<Character> = "Ada"::characters()

expect letters::is(["A", "d", "a"])

character

on String

Answers the character at the given position, counting from zero.

A negative position counts back from the end: -1 is the last character. A position outside the String answers nothing, and the defaultingTo: entry answers the given character instead.

character(at index: Integer) -> Optional<Character>
NameTypeDescription
atIntegerthe position to read

the character, or nothing when the position is outside the String.

Example
expect "Ada"::character(at 0)::is("A")
expect "Ada"::character(at -1)::is("a")
expect "Ada"::character(at 3)::isEmpty()
character(at index: Integer, defaultingTo fallback: Character) -> Character
NameTypeDescription
atIntegerthe position to read
defaultingToCharacterthe character to answer with when the position is outside the String

the character, or the given one in its place.

Example
expect "Ada"::character(at 1, defaultingTo "?")::is("d")
expect "Ada"::character(at 3, defaultingTo "?")::is("?")

firstCharacter

on String

Answers the first character of the String.

The empty String has no first character and answers nothing. The defaultingTo: entry answers the given character instead.

firstCharacter() -> Optional<Character>

the first character, or nothing when the String is empty.

Example
expect "Ada"::firstCharacter()::is("A")
expect ""::firstCharacter()::isEmpty()
firstCharacter(defaultingTo fallback: Character) -> Character
NameTypeDescription
defaultingToCharacterthe character to answer with when the String is empty

the first character, or the given one in its place.

Example
constant name: String = "Ada"

expect name::firstCharacter(defaultingTo "?")::is("A")
expect ""::firstCharacter(defaultingTo "?")::is("?")

on NonEmptyString

firstCharacter() -> Character

Answers the first character of the String.

The String has a character in it, so there is a first one to answer.

the first character.

Example
constant initial: Character = "Ada"::firstCharacter()

expect initial::is("A")

lastCharacter

on String

Answers the last character of the String.

The empty String has no last character and answers nothing. The defaultingTo: entry answers the given character instead.

lastCharacter() -> Optional<Character>

the last character, or nothing when the String is empty.

Example
expect "Ada"::lastCharacter()::is("a")
expect ""::lastCharacter()::isEmpty()
lastCharacter(defaultingTo fallback: Character) -> Character
NameTypeDescription
defaultingToCharacterthe character to answer with when the String is empty

the last character, or the given one in its place.

Example
constant name: String = "Ada"

expect name::lastCharacter(defaultingTo "?")::is("a")
expect ""::lastCharacter(defaultingTo "?")::is("?")

on NonEmptyString

lastCharacter() -> Character

Answers the last character of the String.

The String has a character in it, so there is a last one to answer.

the last character.

Example
constant final: Character = "Ada"::lastCharacter()

expect final::is("a")

codePoints

on String

codePoints() -> List<Integer>

Answers the Unicode code points of the String.

A character is a grapheme cluster and a code point is one point of Unicode. The two counts differ wherever a character is built out of several points. A four-person emoji is one character and seven points.

the List of code points, read off the composed form.

Example
expect "ab"::codePoints()::is([97, 98])

Building

prepend

on String

prepend(_ other: String) -> String

Joins another String onto the front of this one.

NameTypeDescription
_Stringthe String to add to the front

the two Strings joined together.

Example
expect "world"::prepend("hello ")::is("hello world")

append

on String

append(_ other: String) -> String

Joins another String onto the end of this one.

NameTypeDescription
_Stringthe String to add to the end

the two Strings joined together.

Example
expect "hello"::append(" world")::is("hello world")

repeat

on String

repeat(times count: Integer) -> String

Answers the String joined to itself the given number of times.

NameTypeDescription
timesIntegerhow many copies to join

the repeated String. A count below one answers the empty String.

Example
expect "ha"::repeat(times 3)::is("hahaha")
expect "ha"::repeat(times 0)::isEmpty()

on NonEmptyString

repeat(times count: PositiveInteger) -> NonEmptyString

Answers the String joined to itself the given number of times.

The count is above zero, so the answer holds at least one copy of a String that has a character.

NameTypeDescription
timesPositiveIntegerhow many copies to join, proven to be above zero

the repeated String, which is never empty.

Example
constant laugh: NonEmptyString = "ha"::repeat(times 3)

expect laugh::is("hahaha")

pad

on String

pad(
	to length: Integer,
	with padding: String = " ",
	at side: Side = #Start,
) -> String

Answers the String padded with the given String up to the given length.

The padding goes at the front when no end is named. #BothEnds centres the String.

NameTypeDescription
toIntegerthe length to reach
withString = " "the String to pad with, repeated as needed; a space when it is left out.
atSide = #Startthe end to pad; #Start when it is left out.

the padded String; unchanged when it is already that long.

Example
expect "7"::pad(to 3, with "0")::is("007")
expect "Ada"::pad(to 7, with "*", at #BothEnds)::is("**Ada**")
expect "Grace"::pad(to 3)::is("Grace")

indent

on String

indent(by depth: Integer, with unit: String = "  ") -> String

Answers the String with every line moved in by the given depth.

A line that is empty is left as it is, so no line gains whitespace that carries no text. The answer joins the lines with a line feed, whichever line break the String was written with.

NameTypeDescription
byIntegerhow many units to move each line in by
withString = " "the unit to move by, repeated as needed; two spaces when it is left out

the indented String.

Example
expect "text"::indent(by 2)::is("    text")

separate

on String

separate(
	every size: PositiveInteger,
	with separator: String,
	from side: Side = #End,
) -> String

Answers the String with a separator put in every so many characters.

The separator goes between the groups and never at either end. A String no longer than one group answers itself.

NameTypeDescription
everyPositiveIntegerhow many characters a group holds, proven to be above zero
withStringthe separator to put between the groups
fromSide = #Endthe end to count the groups from; #End when it is left out

the grouped String.

Example
expect "1234567"::separate(every 3, with ",")::is("1,234,567")
expect "1234567"::separate(every 3, with ",", from #Start)::is("123,456,7")

Cutting

split

on String

Splits the String at every occurrence of the given separator.

join(with:) on the answer rebuilds the String. A separator with a character in it always leaves at least one piece, and the entry taking a proven separator answers that.

split(on separator: String) -> List<String>
NameTypeDescription
onStringthe separator to split at

the List of pieces, without the separator.

Example
expect "a,b,c"::split(on ",")::is(["a", "b", "c"])
expect "a,,b"::split(on ",")::join(with ",")::is("a,,b")
split(on separator: NonEmptyString) -> NonEmptyList<String>

Splits the String at every occurrence of a separator proven to have a character.

Such a separator cuts between the pieces, and the piece before the first cut is always there. So the answer is never empty.

NameTypeDescription
onNonEmptyStringthe separator to split at, proven to have a character

the List of pieces, which always holds at least one.

Example
expect "a,b"::split(on ",")::is(["a", "b"])
expect ""::split(on ",")::is([""])
split(
	onFirst separator: String,
) -> Optional<{ leading: String, trailing: String }>

Splits the String at the first occurrence of the given separator.

The two pieces are the text before the separator and all the text after it. A separator that does not occur answers nothing. The empty separator occurs at the front, so it answers the empty String and the whole receiver.

NameTypeDescription
onFirstStringthe separator to cut at

the text before and after the separator, or nothing when it does not occur.

Example
constant pair = "key=a=b"::split(onFirst "=")

expect pair::hasValue(where (halves) { <- halves.trailing::is("a=b") })
split(
	onLast separator: String,
) -> Optional<{ leading: String, trailing: String }>

Splits the String at the last occurrence of the given separator.

The two pieces are the text before the separator and all the text after it. A separator that does not occur answers nothing. The empty separator occurs at the end, so it answers the whole receiver and the empty String.

NameTypeDescription
onLastStringthe separator to cut at

the text before and after the separator, or nothing when it does not occur.

Example
constant parts = "archive.tar.gz"::split(onLast ".")

expect parts::is({ leading = "archive.tar", trailing = "gz" })
expect "README"::split(onLast ".")::isEmpty()
split(
	on separator: NonEmptyString,
	atMost count: PositiveInteger,
) -> NonEmptyList<String>

Splits the String at the separator, into no more than the given number of pieces.

The last piece keeps every separator the split stopped short of. A count of one answers the receiver alone.

NameTypeDescription
onNonEmptyStringthe separator to split at, proven to have a character
atMostPositiveIntegerhow many pieces to answer at the most, proven to be above zero

the List of pieces, which always holds at least one.

Example
expect "a=b=c"::split(on "=", atMost 2)::is(["a", "b=c"])

trim

on String

trim(at side: Side = #BothEnds) -> String

Answers the String without the whitespace around it.

The whitespace goes from both ends when no end is named.

NameTypeDescription
atSide = #BothEndsthe end to trim; #BothEnds when it is left out.

the trimmed String.

Example
expect "  Ada  "::trim()::is("Ada")
expect "  Ada  "::trim(at #End)::is("  Ada")

slice

on String

slice(from start: Integer = 0, to end: Integer = @::length()) -> String

Answers the characters from one position up to, but not including, another.

A negative position counts back from the end, so slice(from 0, to -1) drops the last character. An empty or inverted range answers the empty String.

NameTypeDescription
fromInteger = 0the first position to include; zero when it is left out.
toInteger = @::length()the position to stop before; the length when it is left out.

the String of that range of characters.

Example
expect "Lions"::slice(from 1, to 3)::is("io")
expect "Lions"::slice(to -1)::is("Lion")
expect "Lions"::slice(from 3, to 1)::isEmpty()

remove

on String

Answers the String without the given prefix or suffix.

The String comes back unchanged when it does not begin or end with the given one. The empty String is a prefix and a suffix of every String, and taking it away changes nothing.

Example
expect "/api/users"::remove(prefix "/api")::is("/users")
expect "String.es"::remove(suffix ".es")::is("String")
remove(prefix part: String) -> String
NameTypeDescription
prefixStringthe String to take off the front

the String without that prefix.

remove(suffix part: String) -> String
NameTypeDescription
suffixStringthe String to take off the end

the String without that suffix.

truncate

on String

truncate(to length: Integer, with ellipsis: String = "…") -> String

Answers the String cut down to the given length, ending with an ellipsis.

The ellipsis counts toward the length, so the answer is never longer than the length asked for. A String already that short answers itself. A length that leaves no room for the ellipsis answers the ellipsis cut to the length, and a length below one answers the empty String.

NameTypeDescription
toIntegerthe length to cut down to
withString = "…"the String to end with; a horizontal ellipsis when it is left out

the cut String, of that length at the most.

Example
expect "Hello, World"::truncate(to 8)::is("Hello, …")
expect "Hello"::truncate(to 8)::is("Hello")

Changing case and form

uppercase

on String

uppercase() -> String

Answers the String with every character in upper case.

Example
expect "Ada Lovelace"::uppercase()::is("ADA LOVELACE")

on NonEmptyString

uppercase() -> NonEmptyString

Answers the String with every character in upper case.

Case mapping answers a character for every character it is given, so the answer has one too.

the upper-cased String, which is never empty.

Example
constant loud: NonEmptyString = "Ada"::uppercase()

expect loud::is("ADA")

lowercase

on String

lowercase() -> String

Answers the String with every character in lower case.

Example
expect "Ada Lovelace"::lowercase()::is("ada lovelace")

on NonEmptyString

lowercase() -> NonEmptyString

Answers the String with every character in lower case.

Case mapping answers a character for every character it is given, so the answer has one too.

the lower-cased String, which is never empty.

Example
constant quiet: NonEmptyString = "ADA"::lowercase()

expect quiet::is("ada")

normalize

on String

normalize(as form: NormalizationForm = #ComposedCanonical) -> String

Answers the String in the given Unicode normalization form.

Two Strings that look the same can then compare and read the same. The default form is the one is and compare already work in.

The two canonical forms are one text to every other Method here. They differ in the bytes Terminal.write emits, so normalize(as #DecomposedCanonical)::length() answers what length() answered before. The two compatibility forms do change the text.

NameTypeDescription
asNormalizationForm = #ComposedCanonicalthe normalization form to produce; #ComposedCanonical when it is left out.

the normalized String.

Example
expect "x²"::normalize(as #ComposedCompatibility)::is("x2")
expect "é"::normalize(as #DecomposedCanonical)::length()::is(1)

replaceEvery

on String

Answers the String with every occurrence of one part replaced by another.

The occurrences do not overlap. The empty String matches nowhere, so the String comes back unchanged.

replaceEvery(_ part: String, with replacement: String) -> String
NameTypeDescription
_Stringthe String to look for
withStringthe String to put in its place

the String with the replacements made.

Example
expect "2026-10-09"::replaceEvery("-", with "/")::is("2026/10/09")
expect "2026-10-09"::replaceEvery("", with "/")::is("2026-10-09")
replaceEvery(
	_ part: String,
	with replacement: String,
	comparing sensitivity: CaseSensitivity,
) -> String
NameTypeDescription
_Stringthe String to look for
withStringthe String to put in its place
comparingCaseSensitivitywhether case is significant

the String with the replacements made under the given CaseSensitivity.

Example
constant laugh = "Ho ho HO"::replaceEvery(
	"ho",
	with "ha",
	comparing #Insensitive,
)

expect laugh::is("ha ha ha")

replaceFirst

on String

Answers the String with the first occurrence of one part replaced by another.

The empty String matches nowhere, so it and a part that does not occur leave the String unchanged.

replaceFirst(_ part: String, with replacement: String) -> String
NameTypeDescription
_Stringthe String to look for
withStringthe String to put in its place

the String with the first replacement made.

Example
expect "2026-10-09"::replaceFirst("-", with "/")::is("2026/10-09")
expect "2026"::replaceFirst("-", with "/")::is("2026")
replaceFirst(
	_ part: String,
	with replacement: String,
	comparing sensitivity: CaseSensitivity,
) -> String
NameTypeDescription
_Stringthe String to look for
withStringthe String to put in its place
comparingCaseSensitivitywhether case is significant

the String with the first replacement made under the given CaseSensitivity.

Example
constant laugh = "Ho ho HO"::replaceFirst(
	"ho",
	with "ha",
	comparing #Insensitive,
)

expect laugh::is("ha ho HO")

reverse

on String

reverse() -> String

Answers the String with its characters in the opposite order.

Example
expect "stressed"::reverse()::is("desserts")

on NonEmptyString

reverse() -> NonEmptyString

Answers the String with its characters in the opposite order.

Reversing keeps every character, so the answer has one too.

the reversed String, which is never empty.

Example
constant word: NonEmptyString = "drawer"::reverse()

expect word::is("reward")

capitalize

on String

capitalize() -> String

Answers the String with its first character in upper case.

Every other character is left as it stands. The empty String answers itself.

the String with an upper-case first character.

Example
expect "lions"::capitalize()::is("Lions")

quote

on String

quote() -> String

Answers the String as a Program would write it down.

The text is put in quotes, and anything a String Literal has to escape is escaped. A character with no spelling of its own is written as its code point.

the quoted String.

Example
expect "ab"::quote()::is("\"ab\"")

Refinements

Each type below is a checked refinement: a type with a condition. A value has the type only once the compiler knows the condition holds, and Checked refinements shows how it finds out. Under each one, Tighter here lists the methods above that return a more precise type when called on it. Returned by lists the methods that return it.

NonEmptyString

type NonEmptyString = String where @::hasCharacters()

A String proven to have a character in it, as a checked refinement of String.

The proof is what lets length answer above zero, and firstCharacter and lastCharacter answer a character rather than an Optional. A String written down with a character in it carries the proof. A String a Program is handed earns it through an if asking hasCharacters, or the else of one asking isEmpty.

split(on:) reads the proof on its separator rather than on the receiver. A separator with a character in it always leaves a piece, so the answer is a NonEmptyList.

Tighter here length, characters, firstCharacter, lastCharacter, repeat, uppercase, lowercase, reverse

Returned by NonEmptyString::uppercase(), NonEmptyString::lowercase(), NonEmptyString::reverse(), NonEmptyString::repeat(times:)

Character

type Character = String where @::isOneCharacter()

A String of one character, as a checked refinement of String.

A character is a Unicode grapheme cluster, which is the unit every position Method here counts. The proof is what lets characters and character(at:) name what they answer. A String written down with one character in it carries the proof.

A Character is a String, so it answers everything a String answers and goes wherever a String is wanted.

Returned by String::characters(), String::character(at:), String::character(at:defaultingTo:), String::firstCharacter(), String::firstCharacter(defaultingTo:), String::lastCharacter(), String::lastCharacter(defaultingTo:), NonEmptyString::characters(), NonEmptyString::firstCharacter(), NonEmptyString::lastCharacter()

Modes

Each mode below is a choice whose cases carry no payload. You pass one to a method to choose how the method works. Every mode has is, isNot and toString, and cases() on its name lists its cases, as in Side.cases(). None of them is written out for a mode: Protocols says where each one comes from.

Side

choice Side {
	Start,
	End,
	BothEnds,
}

Which end of a String a Method works on: the start, the end, or both ends.

trim works on both ends when a call names no side, and pad works on the start.

Cases #Start, #End, #BothEnds

Taken by

CaseSensitivity

choice CaseSensitivity {
	Sensitive,
	Insensitive,
}

Whether a String comparison treats upper and lower case as the same.

#Sensitive tells the two apart, and #Insensitive does not. A comparison that names no sensitivity is sensitive.

Cases #Sensitive, #Insensitive

Taken by

NormalizationForm

choice NormalizationForm {
	ComposedCanonical,
	DecomposedCanonical,
	ComposedCompatibility,
	DecomposedCompatibility,
}

Which Unicode normalization form normalize(as:) produces.

The canonical forms, NFC and NFD, keep the text as it reads. Composed joins a base and its marks, and Decomposed splits them apart. The compatibility forms, NFKC and NFKD, also fold ligatures and superscripts onto plain equivalents, which changes the text.

Cases #ComposedCanonical, #DecomposedCanonical, #ComposedCompatibility, #DecomposedCompatibility

Taken by

esc