String

A built-in representation for efficient string manipulation. When it comes to strings, there are three concepts worth knowing about:

  • Code units: represents the smallest primitive value of a string. In Gren, code units are represented by a 16-bit value. This is enough to store the most common characters in western languages (Latin, Greek, Cyrilic), but not all unicode characters.
  • Code points: represents a unicode character. Code points can be represented by one unit, or a pair of units.
  • Graphemes: represents a single visual glyph, like certain emojis or characters with accents.

Unless otherwise noted, all functions in this module deal with code points.

type String

A String is a chunk of text. String literals are enclosed in "double quotes".

"Hello!"

"How are you?"

"🙈🙉🙊"

-- strings with escape characters
"this\n\t\"that\""

"🙈🙉🙊" -- "🙈🙉🙊"

-- multiline strings
"""Triple double quotes let you
create "multiline strings" which
can have unescaped quotes and newlines.
"""

A String can represent any sequence of unicode characters. You can use the unicode escapes from \u{0000} to \u{10FFFF} to represent characters by their code point. You can also include the unicode characters directly. Using the escapes can be better if you need one of the many whitespace characters with different widths.

Note: JavaScript lets you use double quotes and single quotes interchangably. This is not true in Gren. You must use double quotes for a String, and you must use single quotes for a Char.

isEmpty : String -> Bool

Determine if a string is empty.

isEmpty "" == True

isEmpty "the world" == False
count : String -> Int

Count the number of characters in a string.

count "innumerable" == 11

count "" == 0
reverse : String -> String

Reverse a string.

reverse "stressed" == "desserts"
repeat : Int -> String -> String

Repeat a string n times.

repeat 3 "ha" == "hahaha"
replace : String -> String -> String -> String

Replace all occurrences of some substring.

replace "." "-" "Json.Decode.succeed" == "Json-Decode-succeed"

replace "," "/" "a,b,c,d,e" == "a/b/c/d/e"

Note: If you need more advanced replacements, check out the gren-lang/parser package or String.Regex module.

Building and Splitting

prepend : String -> String -> String

Combine two strings. You can also use the (++) operator to do this.

prepend "butter" "fly" == "butterfly"
append : String -> String -> String

Append one string onto another. This is the same operation as prepend, but with the arguments reversed.

append "butter" "fly" == "flybutter"
split : String -> String -> Array String

Split a string using a given separator. If the seperator doesn't appear, you will get an array containing the original string.

split "," "" == [""]

split "," "cat" == ["cat"]

split "," "cat,dog,cow" == [ "cat", "dog", "cow" ]

split "/" "home/evan/Desktop/" == [ "home", "evan", "Desktop", "" ]
join : String -> Array String -> String

Put many strings together with a given separator.

join "a" [ "H", "w", "ii", "n" ] == "Hawaiian"

join " " [ "cat", "dog", "cow" ] == "cat dog cow"

join "/" [ "home", "evan", "Desktop" ] == "home/evan/Desktop"
words : String -> Array String

Break a string into words, splitting on chunks of whitespace.

words "How are \t you? \n Good?" == [ "How", "are", "you?", "Good?" ]
lines : String -> Array String

Break a string into lines, splitting on newlines.

lines "How are you?\nGood?" == [ "How are you?", "Good?" ]

Get Substrings

slice : Int -> Int -> String -> String

Take a substring given a start and end index. Negative indexes are taken starting from the end of the array.

slice 7 9 "snakes on a plane!" == "on"

slice 0 6 "snakes on a plane!" == "snakes"

slice 0 -7 "snakes on a plane!" == "snakes on a"

slice -6 -1 "snakes on a plane!" == "plane"
takeFirst : Int -> String -> String

Make a new string using the first n characters. If n is larger than the length of the string, then the string is returned as is.

takeFirst 2 "Mulder" == "Mu"

takeFirst 8 "Mulder" == "Mulder"
takeLast : Int -> String -> String

Make a new string using the last n characters. If n is larger than the length of the string, then the string is returned as is.

takeLast 2 "Scully" == "ly"

takeLast 8 "Scully" == "Scully"
dropFirst : Int -> String -> String

Drop the first n characters.

dropFirst 2 "The Lone Gunmen" == "e Lone Gunmen"
dropLast : Int -> String -> String

Drop the last n characters.

dropLast 2 "Cigarette Smoking Man" == "Cigarette Smoking M"

Check for Substrings

contains : String -> String -> Bool

See if the second string contains the first one.

contains "the" "theory" == True

contains "hat" "theory" == False

contains "THE" "theory" == False
startsWith : String -> String -> Bool

See if the second string starts with the first one.

startsWith "the" "theory" == True

startsWith "ory" "theory" == False
endsWith : String -> String -> Bool

See if the second string ends with the first one.

endsWith "the" "theory" == False

endsWith "ory" "theory" == True
firstIndexOf : String -> String -> Maybe Int

Find the index of the first string within the second one, if it's there.

indexOf "the" "theory" == Just 0

indexOf "ory" "theory" == Just 3

indexOf "a" "theory" == Nothing
lastIndexOf : String -> String -> Maybe Int

Find the last index of the first string within the second one, if it's there.

lastIndexOf "abra" "abracadabra" == Just 7

lastIndexOf "barb" "abracadabra" == Nothing
indices : String -> String -> Array Int

Get all of the indices for a substring in another string.

indexes "i" "Mississippi" == [ 1, 4, 7, 10 ]

indexes "ss" "Mississippi" == [ 2, 5 ]

indexes "needle" "haystack" == []

Int Conversions

toInt : String -> Maybe Int

Try to convert a string into an int, failing on improperly formatted strings.

String.toInt "123" == Just 123

String.toInt "-42" == Just -42

String.toInt "3.1" == Nothing

String.toInt "31a" == Nothing

If you are extracting a number from some raw user input, you will typically want to use Maybe.withDefault to handle bad data:

Maybe.withDefault 0 (String.toInt "42") == 42

Maybe.withDefault 0 (String.toInt "ab") == 0
fromInt : Int -> String

Convert an Int to a String.

String.fromInt 123 == "123"

String.fromInt -42 == "-42"

Check out Debug.toString to convert any value to a string for debugging purposes.

Float Conversions

toFloat : String -> Maybe Float

Try to convert a string into a float, failing on improperly formatted strings.

String.toFloat "123" == Just 123.0

String.toFloat "-42" == Just -42.0

String.toFloat "3.1" == Just 3.1

String.toFloat "31a" == Nothing

If you are extracting a number from some raw user input, you will typically want to use Maybe.withDefault to handle bad data:

Maybe.withDefault 0 (String.toFloat "42.5") == 42.5

Maybe.withDefault 0 (String.toFloat "cats") == 0
fromFloat : Float -> String

Convert a Float to a String.

String.fromFloat 123 == "123"

String.fromFloat -42 == "-42"

String.fromFloat 3.9 == "3.9"

Check out Debug.toString to convert any value to a string for debugging purposes.

Char Conversions

fromChar : Char -> String

Create a string from a given character.

fromChar 'a' == "a"
pushFirst : Char -> String -> String

Add a character to the beginning of a string.

pushFirst 'T' "he truth is out there" == "The truth is out there"
pushLast : Char -> String -> String

Add a character to the end of a string.

pushLast 'T' "he truth is out there" == "he truth is out thereT"
popFirst : String -> Maybe { first : Char, rest : String }

Split a non-empty string into its first character and its remaining characters. This lets you pattern match on strings exactly as you would with arrays.

popFirst "abc" == Just { first = 'a', rest = "bc" }

popFirst "" == Nothing
popLast : String -> Maybe { last : Char, rest : String }

Split a non-empty string into its last character and its remaining characters. This lets you pattern match on strings exactly as you would with arrays.

popLast "abc" == Just { first = 'c', rest = "ab" }

popLast "" == Nothing

Array Conversions

toArray : String -> Array Char

Convert a string to an array of characters.

toArray "abc" == [ 'a', 'b', 'c' ]

toArray "🙈🙉🙊" == [ '🙈', '🙉', '🙊' ]
fromArray : Array Char -> String

Convert an array of characters into a String.

fromArray [ 'a', 'b', 'c' ] == "abc"

fromArray [ '🙈', '🙉', '🙊' ] == "🙈🙉🙊"

Formatting

Cosmetic operations such as padding with extra characters or trimming whitespace.

toUpper : String -> String

Convert a string to all upper case. Useful for case-insensitive comparisons and VIRTUAL YELLING.

toUpper "skinner" == "SKINNER"
toLower : String -> String

Convert a string to all lower case. Useful for case-insensitive comparisons.

toLower "X-FILES" == "x-files"
pad : Int -> Char -> String -> String

Pad a string on both sides until it has a given length.

pad 5 ' ' "1" == "  1  "

pad 5 ' ' "11" == "  11 "

pad 5 ' ' "121" == " 121 "
padLeft : Int -> Char -> String -> String

Pad a string on the left until it has a given length.

padLeft 5 '.' "1" == "....1"

padLeft 5 '.' "11" == "...11"

padLeft 5 '.' "121" == "..121"
padRight : Int -> Char -> String -> String

Pad a string on the right until it has a given length.

padRight 5 '.' "1" == "1...."

padRight 5 '.' "11" == "11..."

padRight 5 '.' "121" == "121.."
trim : String -> String

Get rid of whitespace on both sides of a string.

trim "  hats  \n" == "hats"
trimLeft : String -> String

Get rid of whitespace on the left of a string.

trimLeft "  hats  \n" == "hats  \n"
trimRight : String -> String

Get rid of whitespace on the right of a string.

trimRight "  hats  \n" == "  hats"

Higher-Order Functions

map : (Char -> Char) -> String -> String

Transform every character in a string

map
    (\c ->
        if c == '/' then
            '.'

        else
            c
    )
    "a/b/c"
    == "a.b.c"
keepIf : (Char -> Bool) -> String -> String

Keep only the characters that pass the test.

keepIf isDigit "R2-D2" == "22"
foldl : (Char -> b -> b) -> b -> String -> b

Reduce a string from the beginning.

foldl cons "" "time" == "emit"
foldr : (Char -> b -> b) -> b -> String -> b

Reduce a string from the end.

foldr cons "" "time" == "time"
any : (Char -> Bool) -> String -> Bool

Determine whether any characters pass the test.

any isDigit "90210" == True

any isDigit "R2-D2" == True

any isDigit "heart" == False

any isDigit "" == False
all : (Char -> Bool) -> String -> Bool

Determine whether all characters pass the test.

all isDigit "90210" == True

all isDigit "R2-D2" == False

all isDigit "heart" == False

all isDigit "" = True

Char Units

Functions that operates on units instead of code points.

unitLength : String -> Int

Get the number of character units in a string. As strings are, essentially, arrays of character units, this is a constant time operation.

getUnit : Int -> String -> Maybe Char

Retrieve the character unit at a given index, or Nothing if the index is out of bounds. A negative index uses the end of the string as the starting point.

getUnit 1 "abc" == Just 'a'

getUnit 10 "abc" == Nothing

getUnit -1 "abc" == Just 'c'
foldlUnits : (Char -> b -> b) -> b -> String -> b

Reduce a string from the beginning. The given function will receive character units instead of a code point, meaning that the provided Char could possibly represent one half of a full character.

foldlUnits pushFirst "" "time" == "emit"
foldrUnits : (Char -> b -> b) -> b -> String -> b

Reduce a string from the end. The given function will receive character units instead of a code point, meaning that the provided Char could possibly represent one half of a full character.

foldrUnits pushFirst "" "time" == "time"