How do I work with strings in EK9?
← Getting Started · Ref: Q37
EK9 strings use double quotes for plain text and backticks for interpolation. Strings are streamable as character sequences, so substring extraction and text analysis use the same stream pipeline syntax as collections.
LITERALS AND INTERPOLATION
greeting <- "Hello World" stdout.println(`Welcome to ${lang} version ${ver}`)
Escape sequences: \t
\\ \" \uXXXX.
KEY METHODS
.upperCase(), .lowerCase(), .trim(), .length(), .count(char)
.rightPadded(n), .leftPadded(n)
Concatenation with +. Use 'ek9 -h String' for the full API.
STREAMABLE STRINGS
Strings stream as characters using skip/head/map/filter/collect:
brownFox <- cat sentence | skip 10 | head 9 | collect as String
This replaces substring() with unambiguous skip-and-take semantics. Works identically on strings and collections.
Comparison: ==, <>, <, >, <=, >=, <=>. Hashcode: #?.
See Q33 for regex matching. See Q38 for the Character type. See Q43 for backtick escaping. See Q44 for locale formatting. See Q113 for text construct. See Q170 for string length. See Q171 for string contains. See Q242 for conversion operators.
Example
defines module qa.strings defines function charToLowerCaseString() -> ch as Character <- result as String: ch? <- $ch.lowerCase() else String() stringComparator() -> s1 as String s2 as String <- comparison as Integer: s1 <=> s2 justStringValue() -> s1 as String <- rtn as String: s1 isO() -> ch as Character <- found as Integer: ch == 'o' or ch == 'O' <- 1 else Integer() defines program StringDemo() stdout <- Stdout() // === STRING LITERALS === // Double-quoted strings greeting <- "Hello World" stdout.println(greeting) // String interpolation with backticks languageName <- "EK9" versionNumber <- 1 stdout.println(`Welcome to ${languageName} version ${versionNumber}`) // Escape sequences: \t tab, \n newline, \\ backslash, \" quote withTab <- "Column1\tColumn2" stdout.println(withTab) withNewline <- "Line1\nLine2" stdout.println(withNewline) // Unicode escapes for non-ASCII characters danish <- "\u00C6\u00D8\u00C5" stdout.println(`Danish letters: ${danish}`) // Unset string unsetString <- String() require ~unsetString? // === STRING METHODS === sentence <- "The Quick Brown Fox" // Case conversion stdout.println(`Upper: ${sentence.upperCase()}`) stdout.println(`Lower: ${sentence.lowerCase()}`) // Padding stdout.println(`Right padded: [${sentence.rightPadded(30)}]`) stdout.println(`Left padded: [${sentence.leftPadded(30)}]`) // Trimming paddedSentence <- sentence.rightPadded(30) stdout.println(`Trimmed: [${paddedSentence.trim()}]`) // Length — two equivalent syntaxes stdout.println(`Length (method): ${sentence.length()}`) stdout.println(`Length (operator): ${length sentence}`) // Count character occurrences stdout.println(`Count of 'o': ${sentence.count('o')}`) // === CONCATENATION === part1 <- "The Quick Brown Fox" part2 <- "Jumps Over The Lazy Dog" fullSentence <- `${part1} ${part2}` stdout.println(fullSentence) // === HASHCODE === hashValue <- #? sentence stdout.println(`Hashcode: ${hashValue}`) // === STRINGS ARE STREAMABLE === // Strings stream as character sequences // Extract a substring using skip and head brownFox <- cat fullSentence | skip 10 | head 9 | collect as String stdout.println(`Extracted: [${brownFox}]`) // Going beyond string length is safe — returns what is available endPart <- cat fullSentence | skip 35 | head 20 | collect as String stdout.println(`End part: [${endPart}]`) // Join a list of strings via stream collect spacer <- " " wordParts <- [part1, spacer, part2] rejoined <- cat wordParts | collect as String stdout.println(`Rejoined: ${rejoined}`) // Count occurrences of o/O using stream map oCount <- cat fullSentence | map with isO | collect as Integer stdout.println(`Count of o/O: ${oCount}`) // Find unique characters used (sorted, lowercase) charsUsed <- cat fullSentence | map with charToLowerCaseString | sort with stringComparator | uniq by justStringValue | collect as String stdout.println(`Unique chars: [${charsUsed}]`) // === COMPARISON === alpha <- "abc" beta <- "def" require alpha < beta require beta > alpha require alpha <> beta require alpha == "abc" require alpha <= "abc" require alpha >= "abc" // Min operator fruit1 <- "apple" fruit2 <- "banana" lesser <- fruit1 <? fruit2 require lesser == "apple" stdout.println(`Min: ${lesser}`)
Common mistakes
E07620 — The + operator on String requires another String operand. Adding an Integer to a String triggers E07620 — type incompatibility. Use $ to convert to string first: "Hello" + $number. See ek9 -h E07620 for details.
Incorrect:
greeting <- "Hello World" + 42
Correct:
greeting <- "Hello World"
Other ways to ask this
- How does string interpolation work in EK9?
- Can I use strings as streams of characters in EK9?
- What string methods does EK9 provide?
- How do I extract substrings in EK9?
Coming from another language?
Java: no native interpolation, substring() methods, no streamable characters. Python: f-strings, slicing [start:end]. Kotlin: similar $var templates. EK9: backtick interpolation, strings natively streamable as characters, skip/head replaces substring().
Keywords: stream, pad, backtick, migrate, start, escape, uppercase, unicode, length, trim, interpolation, string, substring, first, intro, count, beginner, concatenation, lowercase