update to support fenced code

This commit is contained in:
Luke Boswell 2023-03-08 17:30:30 +11:00
parent a87794e7b2
commit 890f3db10a
No known key found for this signature in database
GPG key ID: F6DB3C9DB47377B0
10 changed files with 828 additions and 724 deletions

View file

@ -48,24 +48,26 @@ false = @Bool False
## the logic [AND](https://en.wikipedia.org/wiki/Logical_conjunction)
## gate. The infix operator `&&` can also be used as shorthand for
## `Bool.and`.
##
## expect (Bool.and Bool.true Bool.true) == Bool.true
## expect (Bool.true && Bool.true) == Bool.true
## expect (Bool.false && Bool.true) == Bool.false
## expect (Bool.true && Bool.false) == Bool.false
## expect (Bool.false && Bool.false) == Bool.false
##
## ```
## expect (Bool.and Bool.true Bool.true) == Bool.true
## expect (Bool.true && Bool.true) == Bool.true
## expect (Bool.false && Bool.true) == Bool.false
## expect (Bool.true && Bool.false) == Bool.false
## expect (Bool.false && Bool.false) == Bool.false
## ```
##
## **Performance Note** that in Roc the `&&` and `||` work the same way as any
## other function. However, in some languages `&&` and `||` are special-cased.
## In these languages the compiler will skip evaluating the expression after the
## first operator under certain circumstances. For example an expression like
## `enablePets && likesDogs user` would compile to.
##
## if enablePets then
## likesDogs user
## else
## Bool.false
##
## ```
## if enablePets then
## likesDogs user
## else
## Bool.false
## ```
## Roc does not do this because conditionals like `if` and `when` have a
## performance cost. Calling a function can sometimes be faster across the board
## than doing an `if` to decide whether to skip calling it.
@ -74,13 +76,13 @@ and : Bool, Bool -> Bool
## Returns `Bool.true` when either input is a `Bool.true`. This is equivalent to
## the logic [OR](https://en.wikipedia.org/wiki/Logical_disjunction) gate.
## The infix operator `||` can also be used as shorthand for `Bool.or`.
##
## expect (Bool.or Bool.false Bool.true) == Bool.true
## expect (Bool.true || Bool.true) == Bool.true
## expect (Bool.false || Bool.true) == Bool.true
## expect (Bool.true || Bool.false) == Bool.true
## expect (Bool.false || Bool.false) == Bool.false
##
## ```
## expect (Bool.or Bool.false Bool.true) == Bool.true
## expect (Bool.true || Bool.true) == Bool.true
## expect (Bool.false || Bool.true) == Bool.true
## expect (Bool.true || Bool.false) == Bool.true
## expect (Bool.false || Bool.false) == Bool.false
## ```
## **Performance Note** that in Roc the `&&` and `||` work the same way as any
## other functions. However, in some languages `&&` and `||` are special-cased.
## Refer to the note in `Bool.and` for more detail.
@ -89,9 +91,10 @@ or : Bool, Bool -> Bool
## Returns `Bool.false` when given `Bool.true`, and vice versa. This is
## equivalent to the logic [NOT](https://en.wikipedia.org/wiki/Negation)
## gate. The operator `!` can also be used as shorthand for `Bool.not`.
##
## expect (Bool.not Bool.false) == Bool.true
## expect (!Bool.false) == Bool.true
## ```
## expect (Bool.not Bool.false) == Bool.true
## expect (!Bool.false) == Bool.true
## ```
not : Bool -> Bool
## This will call the function `Bool.isEq` on the inputs, and then `Bool.not`
@ -101,10 +104,11 @@ not : Bool -> Bool
##
## **Note** that `isNotEq` does not accept arguments whose types contain
## functions.
##
## expect (Bool.isNotEq Bool.false Bool.true) == Bool.true
## expect (Bool.false != Bool.false) == Bool.false
## expect "Apples" != "Oranges"
## ```
## expect (Bool.isNotEq Bool.false Bool.true) == Bool.true
## expect (Bool.false != Bool.false) == Bool.false
## expect "Apples" != "Oranges"
## ```
isNotEq : a, a -> Bool | a has Eq
isNotEq = \a, b -> structuralNotEq a b

View file

@ -6,13 +6,15 @@ interface Box
## the value from the stack to the heap. This may provide a performance
## optimization for advanced use cases with large values. A platform may require
## that some values are boxed.
##
## expect Box.unbox (Box.box "Stack Faster") == "Stack Faster"
## ```
## expect Box.unbox (Box.box "Stack Faster") == "Stack Faster"
## ```
box : a -> Box a
## Returns a boxed value.
##
## expect Box.unbox (Box.box "Stack Faster") == "Stack Faster"
## ```
## expect Box.unbox (Box.box "Stack Faster") == "Stack Faster"
## ```
unbox : Box a -> a
# # we'd need reset/reuse for box for this to be efficient

View file

@ -45,15 +45,15 @@ interface Dict
##
## Here's an example of a dictionary which uses a city's name as the key, and
## its population as the associated value.
##
## populationByCity =
## Dict.empty {}
## |> Dict.insert "London" 8_961_989
## |> Dict.insert "Philadelphia" 1_603_797
## |> Dict.insert "Shanghai" 24_870_895
## |> Dict.insert "Delhi" 16_787_941
## |> Dict.insert "Amsterdam" 872_680
##
## ```
## populationByCity =
## Dict.empty {}
## |> Dict.insert "London" 8_961_989
## |> Dict.insert "Philadelphia" 1_603_797
## |> Dict.insert "Shanghai" 24_870_895
## |> Dict.insert "Delhi" 16_787_941
## |> Dict.insert "Amsterdam" 872_680
## ```
## ### Accessing keys or values
##
## We can use [Dict.keys] and [Dict.values] functions to get only the keys or
@ -66,13 +66,13 @@ interface Dict
## ### Removing
##
## We can remove an element from the dictionary, like so:
##
## populationByCity
## |> Dict.remove "Philadelphia"
## |> Dict.keys
## ==
## ["London", "Amsterdam", "Shanghai", "Delhi"]
##
## ```
## populationByCity
## |> Dict.remove "Philadelphia"
## |> Dict.keys
## ==
## ["London", "Amsterdam", "Shanghai", "Delhi"]
## ```
## Notice that the order has changed. Philadelphia was not only removed from the
## list, but Amsterdam - the last entry we inserted - has been moved into the
## spot where Philadelphia was previously. This is exactly what [Dict.remove]
@ -125,36 +125,39 @@ withCapacity = \_ ->
empty {}
## Returns a dictionary containing the key and value provided as input.
##
## expect
## Dict.single "A" "B"
## |> Bool.isEq (Dict.insert (Dict.empty {}) "A" "B")
## ```
## expect
## Dict.single "A" "B"
## |> Bool.isEq (Dict.insert (Dict.empty {}) "A" "B")
## ```
single : k, v -> Dict k v | k has Hash & Eq
single = \k, v ->
insert (empty {}) k v
## Returns dictionary with the keys and values specified by the input [List].
##
## expect
## Dict.single 1 "One"
## |> Dict.insert 2 "Two"
## |> Dict.insert 3 "Three"
## |> Dict.insert 4 "Four"
## |> Bool.isEq (Dict.fromList [T 1 "One", T 2 "Two", T 3 "Three", T 4 "Four"])
## ```
## expect
## Dict.single 1 "One"
## |> Dict.insert 2 "Two"
## |> Dict.insert 3 "Three"
## |> Dict.insert 4 "Four"
## |> Bool.isEq (Dict.fromList [T 1 "One", T 2 "Two", T 3 "Three", T 4 "Four"])
## ```
fromList : List (T k v) -> Dict k v | k has Hash & Eq
fromList = \data ->
# TODO: make this efficient. Should just set data and then set all indicies in the hashmap.
List.walk data (empty {}) (\dict, T k v -> insert dict k v)
## Returns the number of values in the dictionary.
##
## expect
## Dict.empty {}
## |> Dict.insert "One" "A Song"
## |> Dict.insert "Two" "Candy Canes"
## |> Dict.insert "Three" "Boughs of Holly"
## |> Dict.len
## |> Bool.isEq 3
## ```
## expect
## Dict.empty {}
## |> Dict.insert "One" "A Song"
## |> Dict.insert "Two" "Candy Canes"
## |> Dict.insert "Three" "Boughs of Holly"
## |> Dict.len
## |> Bool.isEq 3
## ```
len : Dict k v -> Nat | k has Hash & Eq
len = \@Dict { size } ->
size
@ -180,13 +183,14 @@ clear = \@Dict { metadata, dataIndices, data } ->
## Iterate through the keys and values in the dictionary and call the provided
## function with signature `state, k, v -> state` for each value, with an
## initial `state` value provided for the first call.
##
## expect
## Dict.empty {}
## |> Dict.insert "Apples" 12
## |> Dict.insert "Orange" 24
## |> Dict.walk 0 (\count, _, qty -> count + qty)
## |> Bool.isEq 36
## ```
## expect
## Dict.empty {}
## |> Dict.insert "Apples" 12
## |> Dict.insert "Orange" 24
## |> Dict.walk 0 (\count, _, qty -> count + qty)
## |> Bool.isEq 36
## ```
walk : Dict k v, state, (state, k, v -> state) -> state | k has Hash & Eq
walk = \@Dict { data }, initialState, transform ->
List.walk data initialState (\state, T k v -> transform state k v)
@ -208,14 +212,15 @@ walkUntil = \@Dict { data }, initialState, transform ->
## Get the value for a given key. If there is a value for the specified key it
## will return [Ok value], otherwise return [Err KeyNotFound].
## ```
## dictionary =
## Dict.empty {}
## |> Dict.insert 1 "Apple"
## |> Dict.insert 2 "Orange"
##
## dictionary =
## Dict.empty {}
## |> Dict.insert 1 "Apple"
## |> Dict.insert 2 "Orange"
##
## expect Dict.get dictionary 1 == Ok "Apple"
## expect Dict.get dictionary 2000 == Err KeyNotFound
## expect Dict.get dictionary 1 == Ok "Apple"
## expect Dict.get dictionary 2000 == Err KeyNotFound
## ```
get : Dict k v, k -> Result v [KeyNotFound] | k has Hash & Eq
get = \@Dict { metadata, dataIndices, data }, key ->
hashKey =
@ -237,12 +242,13 @@ get = \@Dict { metadata, dataIndices, data }, key ->
Err KeyNotFound
## Check if the dictionary has a value for a specified key.
##
## expect
## Dict.empty {}
## |> Dict.insert 1234 "5678"
## |> Dict.contains 1234
## |> Bool.isEq Bool.true
## ```
## expect
## Dict.empty {}
## |> Dict.insert 1234 "5678"
## |> Dict.contains 1234
## |> Bool.isEq Bool.true
## ```
contains : Dict k v, k -> Bool | k has Hash & Eq
contains = \@Dict { metadata, dataIndices, data }, key ->
hashKey =
@ -261,12 +267,13 @@ contains = \@Dict { metadata, dataIndices, data }, key ->
Bool.false
## Insert a value into the dictionary at a specified key.
##
## expect
## Dict.empty {}
## |> Dict.insert "Apples" 12
## |> Dict.get "Apples"
## |> Bool.isEq (Ok 12)
## ```
## expect
## Dict.empty {}
## |> Dict.insert "Apples" 12
## |> Dict.get "Apples"
## |> Bool.isEq (Ok 12)
## ```
insert : Dict k v, k, v -> Dict k v | k has Hash & Eq
insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
hashKey =
@ -305,13 +312,14 @@ insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
insertNotFoundHelper rehashedDict key value h1Key h2Key
## Remove a value from the dictionary for a specified key.
##
## expect
## Dict.empty {}
## |> Dict.insert "Some" "Value"
## |> Dict.remove "Some"
## |> Dict.len
## |> Bool.isEq 0
## ```
## expect
## Dict.empty {}
## |> Dict.insert "Some" "Value"
## |> Dict.remove "Some"
## |> Dict.len
## |> Bool.isEq 0
## ```
remove : Dict k v, k -> Dict k v | k has Hash & Eq
remove = \@Dict { metadata, dataIndices, data, size }, key ->
# TODO: change this from swap remove to tombstone and test is performance is still good.
@ -345,16 +353,17 @@ remove = \@Dict { metadata, dataIndices, data, size }, key ->
## performance optimisation for the use case of providing a default when a value
## is missing. This is more efficient than doing both a `Dict.get` and then a
## `Dict.insert` call, and supports being piped.
## ```
## alterValue : [Present Bool, Missing] -> [Present Bool, Missing]
## alterValue = \possibleValue ->
## when possibleValue is
## Missing -> Present Bool.false
## Present value -> if value then Missing else Present Bool.true
##
## alterValue : [Present Bool, Missing] -> [Present Bool, Missing]
## alterValue = \possibleValue ->
## when possibleValue is
## Missing -> Present Bool.false
## Present value -> if value then Missing else Present Bool.true
##
## expect Dict.update (Dict.empty {}) "a" alterValue == Dict.single "a" Bool.false
## expect Dict.update (Dict.single "a" Bool.false) "a" alterValue == Dict.single "a" Bool.true
## expect Dict.update (Dict.single "a" Bool.true) "a" alterValue == Dict.empty {}
## expect Dict.update (Dict.empty {}) "a" alterValue == Dict.single "a" Bool.false
## expect Dict.update (Dict.single "a" Bool.false) "a" alterValue == Dict.single "a" Bool.true
## expect Dict.update (Dict.single "a" Bool.true) "a" alterValue == Dict.empty {}
## ```
update : Dict k v, k, ([Present v, Missing] -> [Present v, Missing]) -> Dict k v | k has Hash & Eq
update = \dict, key, alter ->
# TODO: look into optimizing by merging substeps and reducing lookups.
@ -369,42 +378,45 @@ update = \dict, key, alter ->
## Returns the keys and values of a dictionary as a [List].
## This requires allocating a temporary list, prefer using [Dict.toList] or [Dict.walk] instead.
##
## expect
## Dict.single 1 "One"
## |> Dict.insert 2 "Two"
## |> Dict.insert 3 "Three"
## |> Dict.insert 4 "Four"
## |> Dict.toList
## |> Bool.isEq [T 1 "One", T 2 "Two", T 3 "Three", T 4 "Four"]
## ```
## expect
## Dict.single 1 "One"
## |> Dict.insert 2 "Two"
## |> Dict.insert 3 "Three"
## |> Dict.insert 4 "Four"
## |> Dict.toList
## |> Bool.isEq [T 1 "One", T 2 "Two", T 3 "Three", T 4 "Four"]
## ```
toList : Dict k v -> List (T k v) | k has Hash & Eq
toList = \@Dict { data } ->
data
## Returns the keys of a dictionary as a [List].
## This requires allocating a temporary [List], prefer using [Dict.toList] or [Dict.walk] instead.
##
## expect
## Dict.single 1 "One"
## |> Dict.insert 2 "Two"
## |> Dict.insert 3 "Three"
## |> Dict.insert 4 "Four"
## |> Dict.keys
## |> Bool.isEq [1,2,3,4]
## ```
## expect
## Dict.single 1 "One"
## |> Dict.insert 2 "Two"
## |> Dict.insert 3 "Three"
## |> Dict.insert 4 "Four"
## |> Dict.keys
## |> Bool.isEq [1,2,3,4]
## ```
keys : Dict k v -> List k | k has Hash & Eq
keys = \@Dict { data } ->
List.map data (\T k _ -> k)
## Returns the values of a dictionary as a [List].
## This requires allocating a temporary [List], prefer using [Dict.toList] or [Dict.walk] instead.
##
## expect
## Dict.single 1 "One"
## |> Dict.insert 2 "Two"
## |> Dict.insert 3 "Three"
## |> Dict.insert 4 "Four"
## |> Dict.values
## |> Bool.isEq ["One","Two","Three","Four"]
## ```
## expect
## Dict.single 1 "One"
## |> Dict.insert 2 "Two"
## |> Dict.insert 3 "Three"
## |> Dict.insert 4 "Four"
## |> Dict.values
## |> Bool.isEq ["One","Two","Three","Four"]
## ```
values : Dict k v -> List v | k has Hash & Eq
values = \@Dict { data } ->
List.map data (\T _ v -> v)
@ -414,24 +426,25 @@ values = \@Dict { data } ->
## both dictionaries will be combined. Note that where there are pairs
## with the same key, the value contained in the second input will be
## retained, and the value in the first input will be removed.
## ```
## first =
## Dict.single 1 "Not Me"
## |> Dict.insert 2 "And Me"
##
## first =
## Dict.single 1 "Not Me"
## |> Dict.insert 2 "And Me"
## second =
## Dict.single 1 "Keep Me"
## |> Dict.insert 3 "Me Too"
## |> Dict.insert 4 "And Also Me"
##
## second =
## Dict.single 1 "Keep Me"
## |> Dict.insert 3 "Me Too"
## |> Dict.insert 4 "And Also Me"
## expected =
## Dict.single 1 "Keep Me"
## |> Dict.insert 2 "And Me"
## |> Dict.insert 3 "Me Too"
## |> Dict.insert 4 "And Also Me"
##
## expected =
## Dict.single 1 "Keep Me"
## |> Dict.insert 2 "And Me"
## |> Dict.insert 3 "Me Too"
## |> Dict.insert 4 "And Also Me"
##
## expect
## Dict.insertAll first second == expected
## expect
## Dict.insertAll first second == expected
## ```
insertAll : Dict k v, Dict k v -> Dict k v | k has Hash & Eq
insertAll = \xs, ys ->
walk ys xs insert
@ -441,18 +454,19 @@ insertAll = \xs, ys ->
## that are in both dictionaries. Note that where there are pairs with
## the same key, the value contained in the first input will be retained,
## and the value in the second input will be removed.
## ```
## first =
## Dict.single 1 "Keep Me"
## |> Dict.insert 2 "And Me"
##
## first =
## Dict.single 1 "Keep Me"
## |> Dict.insert 2 "And Me"
## second =
## Dict.single 1 "Keep Me"
## |> Dict.insert 2 "And Me"
## |> Dict.insert 3 "But Not Me"
## |> Dict.insert 4 "Or Me"
##
## second =
## Dict.single 1 "Keep Me"
## |> Dict.insert 2 "And Me"
## |> Dict.insert 3 "But Not Me"
## |> Dict.insert 4 "Or Me"
##
## expect Dict.keepShared first second == first
## expect Dict.keepShared first second == first
## ```
keepShared : Dict k v, Dict k v -> Dict k v | k has Hash & Eq
keepShared = \xs, ys ->
walk
@ -469,21 +483,22 @@ keepShared = \xs, ys ->
## using the [set difference](https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement)
## of the values. This means that we will be left with only those pairs that
## are in the first dictionary and whose keys are not in the second.
## ```
## first =
## Dict.single 1 "Keep Me"
## |> Dict.insert 2 "And Me"
## |> Dict.insert 3 "Remove Me"
##
## first =
## Dict.single 1 "Keep Me"
## |> Dict.insert 2 "And Me"
## |> Dict.insert 3 "Remove Me"
## second =
## Dict.single 3 "Remove Me"
## |> Dict.insert 4 "I do nothing..."
##
## second =
## Dict.single 3 "Remove Me"
## |> Dict.insert 4 "I do nothing..."
## expected =
## Dict.single 1 "Keep Me"
## |> Dict.insert 2 "And Me"
##
## expected =
## Dict.single 1 "Keep Me"
## |> Dict.insert 2 "And Me"
##
## expect Dict.removeAll first second == expected
## expect Dict.removeAll first second == expected
## ```
removeAll : Dict k v, Dict k v -> Dict k v | k has Hash & Eq
removeAll = \xs, ys ->
walk ys xs (\state, k, _ -> remove state k)

View file

@ -73,11 +73,11 @@ interface List
## Types
## A sequential list of values.
##
## [1, 2, 3] # a list of numbers
## ["a", "b", "c"] # a list of strings
## [[1.1], [], [2.2, 3.3]] # a list of lists of numbers
##
## ```
## [1, 2, 3] # a list of numbers
## ["a", "b", "c"] # a list of strings
## [[1.1], [], [2.2, 3.3]] # a list of lists of numbers
## ```
## The maximum size of a [List] is limited by the amount of heap memory available
## to the current process. If there is not enough memory available, attempting to
## create the list could crash. (On Linux, where [overcommit](https://www.etalabs.net/overcommit.html)
@ -105,11 +105,11 @@ interface List
## will be immediately freed.
##
## Let's look at an example.
## ```
## ratings = [5, 4, 3]
##
## ratings = [5, 4, 3]
##
## { foo: ratings, bar: ratings }
##
## { foo: ratings, bar: ratings }
## ```
## The first line binds the name `ratings` to the list `[5, 4, 3]`. The list
## begins with a refcount of 1, because so far only `ratings` is referencing it.
##
@ -118,14 +118,14 @@ interface List
## refcount getting incremented from 1 to 3.
##
## Let's turn this example into a function.
## ```
## getRatings = \first ->
## ratings = [first, 4, 3]
##
## getRatings = \first ->
## ratings = [first, 4, 3]
##
## { foo: ratings, bar: ratings }
##
## getRatings 5
## { foo: ratings, bar: ratings }
##
## getRatings 5
## ```
## At the end of the `getRatings` function, when the record gets returned,
## the original `ratings =` binding has gone out of scope and is no longer
## accessible. (Trying to reference `ratings` outside the scope of the
@ -140,23 +140,23 @@ interface List
## list, and that list has a refcount of 2.
##
## Let's change the last line to be `(getRatings 5).bar` instead of `getRatings 5`:
## ```
## getRatings = \first ->
## ratings = [first, 4, 3]
##
## getRatings = \first ->
## ratings = [first, 4, 3]
##
## { foo: ratings, bar: ratings }
##
## (getRatings 5).bar
## { foo: ratings, bar: ratings }
##
## (getRatings 5).bar
## ```
## Now, when this expression returns, only the `bar` field of the record will
## be returned. This will mean that the `foo` field becomes inaccessible, causing
## the list's refcount to get decremented from 2 to 1. At this point, the list is back
## where it started: there is only 1 reference to it.
##
## Finally let's suppose the final line were changed to this:
##
## List.first (getRatings 5).bar
##
## ```
## List.first (getRatings 5).bar
## ```
## This call to [List.first] means that even the list in the `bar` field has become
## inaccessible. As such, this line will cause the list's refcount to get
## decremented all the way to 0. At that point, nothing is referencing the list
@ -167,25 +167,25 @@ interface List
## and then with a list of lists, to see how they differ.
##
## Here's the example using a list of numbers.
## ```
## nums = [1, 2, 3, 4, 5, 6, 7]
##
## nums = [1, 2, 3, 4, 5, 6, 7]
##
## first = List.first nums
## last = List.last nums
##
## first
## first = List.first nums
## last = List.last nums
##
## first
## ```
## It makes a list, calls [List.first] and [List.last] on it, and then returns `first`.
##
## Here's the equivalent code with a list of lists:
## ```
## lists = [[1], [2, 3], [], [4, 5, 6, 7]]
##
## lists = [[1], [2, 3], [], [4, 5, 6, 7]]
##
## first = List.first lists
## last = List.last lists
##
## first
## first = List.first lists
## last = List.last lists
##
## first
## ```
## TODO explain how in the former example, when we go to free `nums` at the end,
## we can free it immediately because there are no other refcounts. However,
## in the case of `lists`, we have to iterate through the list and decrement
@ -206,10 +206,11 @@ interface List
## * Roc's compiler optimizes many list operations into in-place mutations behind the scenes, depending on how the list is being used. For example, [List.map], [List.keepIf], and [List.set] can all be optimized to perform in-place mutations.
## * If possible, it is usually best for performance to use large lists in a way where the optimizer can turn them into in-place mutations. If this is not possible, a persistent data structure might be faster - but this is a rare enough scenario that it would not be good for the average Roc program's performance if this were the way [List] worked by default. Instead, you can look outside Roc's standard modules for an implementation of a persistent data structure - likely built using [List] under the hood!
## Check if the list is empty.
## ```
## List.isEmpty [1, 2, 3]
##
## List.isEmpty [1, 2, 3]
##
## List.isEmpty []
## List.isEmpty []
## ```
isEmpty : List a -> Bool
isEmpty = \list ->
List.len list == 0
@ -237,9 +238,9 @@ replace = \list, index, newValue ->
{ list, value: newValue }
## Replaces the element at the given index with a replacement.
##
## List.set ["a", "b", "c"] 1 "B"
##
## ```
## List.set ["a", "b", "c"] 1 "B"
## ```
## If the given index is outside the bounds of the list, returns the original
## list unmodified.
##
@ -249,11 +250,12 @@ set = \list, index, value ->
(List.replace list index value).list
## Add a single element to the end of a list.
## ```
## List.append [1, 2, 3] 4
##
## List.append [1, 2, 3] 4
##
## [0, 1, 2]
## |> List.append 3
## [0, 1, 2]
## |> List.append 3
## ```
append : List a, a -> List a
append = \list, element ->
list
@ -268,11 +270,12 @@ append = \list, element ->
appendUnsafe : List a, a -> List a
## Add a single element to the beginning of a list.
## ```
## List.prepend [1, 2, 3] 0
##
## List.prepend [1, 2, 3] 0
##
## [2, 3, 4]
## |> List.prepend 1
## [2, 3, 4]
## |> List.prepend 1
## ```
prepend : List a, a -> List a
## Returns the length of the list - the number of elements it contains.
@ -289,11 +292,12 @@ withCapacity : Nat -> List a
reserve : List a, Nat -> List a
## Put two lists together.
## ```
## List.concat [1, 2, 3] [4, 5]
##
## List.concat [1, 2, 3] [4, 5]
##
## [0, 1, 2]
## |> List.concat [3, 4]
## [0, 1, 2]
## |> List.concat [3, 4]
## ```
concat : List a, List a -> List a
## Returns the last element in the list, or `ListWasEmpty` if it was empty.
@ -306,17 +310,15 @@ last = \list ->
## A list with a single element in it.
##
## This is useful in pipelines, like so:
##
## websites =
## Str.concat domain ".com"
## |> List.single
##
## ```
## websites =
## Str.concat domain ".com"
## |> List.single
## ```
single : a -> List a
single = \x -> [x]
## Returns a list with the given length, where every element is the given value.
##
##
repeat : a, Nat -> List a
repeat = \value, count ->
repeatHelp value count (List.withCapacity count)
@ -329,8 +331,9 @@ repeatHelp = \value, count, accum ->
accum
## Returns the list with its elements reversed.
##
## List.reverse [1, 2, 3]
## ```
## List.reverse [1, 2, 3]
## ```
reverse : List a -> List a
reverse = \list ->
reverseHelp list 0 (Num.subSaturated (List.len list) 1)
@ -342,12 +345,11 @@ reverseHelp = \list, left, right ->
list
## Join the given lists together into one list.
##
## List.join [[1, 2, 3], [4, 5], [], [6, 7]]
##
## List.join [[], []]
##
## List.join []
## ```
## List.join [[1, 2, 3], [4, 5], [], [6, 7]]
## List.join [[], []]
## List.join []
## ```
join : List (List a) -> List a
join = \lists ->
totalLength =
@ -366,10 +368,10 @@ contains = \list, needle ->
## which updates the `state`. It returns the final `state` at the end.
##
## You can use it in a pipeline:
##
## [2, 4, 8]
## |> List.walk 0 Num.add
##
## ```
## [2, 4, 8]
## |> List.walk 0 Num.add
## ```
## This returns 14 because:
## * `state` starts at 0
## * Each `step` runs `Num.add state elem`, and the return value becomes the new `state`.
@ -385,10 +387,10 @@ contains = \list, needle ->
## 6 | 8 | 14
##
## The following returns -6:
##
## [1, 2, 3]
## |> List.walk 0 Num.sub
##
## ```
## [1, 2, 3]
## |> List.walk 0 Num.sub
## ```
## Note that in other languages, `walk` is sometimes called `reduce`,
## `fold`, `foldLeft`, or `foldl`.
walk : List elem, state, (state, elem -> state) -> state
@ -494,9 +496,9 @@ all = \list, predicate ->
## Run the given function on each element of a list, and return all the
## elements for which the function returned `Bool.true`.
##
## List.keepIf [1, 2, 3, 4] (\num -> num > 2)
##
## ```
## List.keepIf [1, 2, 3, 4] (\num -> num > 2)
## ```
## ## Performance Details
##
## [List.keepIf] always returns a list that takes up exactly the same amount
@ -531,9 +533,9 @@ keepIfHelp = \list, predicate, kept, index, length ->
## Run the given function on each element of a list, and return all the
## elements for which the function returned `Bool.false`.
##
## List.dropIf [1, 2, 3, 4] (\num -> num > 2)
##
## ```
## List.dropIf [1, 2, 3, 4] (\num -> num > 2)
## ```
## ## Performance Details
##
## `List.dropIf` has the same performance characteristics as [List.keepIf].
@ -556,12 +558,13 @@ countIf = \list, predicate ->
## This works like [List.map], except only the transformed values that are
## wrapped in `Ok` are kept. Any that are wrapped in `Err` are dropped.
## ```
## List.keepOks [["a", "b"], [], [], ["c", "d", "e"]] List.last
##
## List.keepOks [["a", "b"], [], [], ["c", "d", "e"]] List.last
##
## fn = \str -> if Str.isEmpty str then Err StrWasEmpty else Ok (Str.len str)
## >>>
## List.keepOks ["", "a", "bc", "", "d", "ef", ""]
## fn = \str -> if Str.isEmpty str then Err StrWasEmpty else Ok (Str.len str)
##
## List.keepOks ["", "a", "bc", "", "d", "ef", ""]
## ```
keepOks : List before, (before -> Result after *) -> List after
keepOks = \list, toResult ->
walker = \accum, element ->
@ -573,12 +576,13 @@ keepOks = \list, toResult ->
## This works like [List.map], except only the transformed values that are
## wrapped in `Err` are kept. Any that are wrapped in `Ok` are dropped.
## ```
## List.keepErrs [["a", "b"], [], [], ["c", "d", "e"]] List.last
##
## List.keepErrs [["a", "b"], [], [], ["c", "d", "e"]] List.last
##
## fn = \str -> if Str.isEmpty str then Err StrWasEmpty else Ok (Str.len str)
## >>>
## List.keepErrs ["", "a", "bc", "", "d", "ef", ""]
## fn = \str -> if Str.isEmpty str then Err StrWasEmpty else Ok (Str.len str)
##
## List.keepErrs ["", "a", "bc", "", "d", "ef", ""]
## ```
keepErrs : List before, (before -> Result * after) -> List after
keepErrs = \list, toResult ->
walker = \accum, element ->
@ -590,10 +594,11 @@ keepErrs = \list, toResult ->
## Convert each element in the list to something new, by calling a conversion
## function on each of them. Then return a new list of the converted values.
## ```
## List.map [1, 2, 3] (\num -> num + 1)
##
## > List.map [1, 2, 3] (\num -> num + 1)
##
## > List.map ["", "a", "bc"] Str.isEmpty
## List.map ["", "a", "bc"] Str.isEmpty
## ```
map : List a, (a -> b) -> List b
## Run a transformation function on the first element of each list,
@ -602,8 +607,9 @@ map : List a, (a -> b) -> List b
##
## Some languages have a function named `zip`, which does something similar to
## calling [List.map2] passing two lists and `Pair`:
##
## zipped = List.map2 ["a", "b", "c"] [1, 2, 3] Pair
## ```
## zipped = List.map2 ["a", "b", "c"] [1, 2, 3] Pair
## ```
map2 : List a, List b, (a, b -> c) -> List c
## Run a transformation function on the first element of each list,
@ -640,25 +646,25 @@ mapWithIndexHelp = \src, dest, func, index, length ->
## Returns a list of all the integers between `start` and `end`.
##
## To include the `start` and `end` integers themselves, use `At` like so:
##
## List.range { start: At 2, end: At 5 } # returns [2, 3, 4, 5]
##
## ```
## List.range { start: At 2, end: At 5 } # returns [2, 3, 4, 5]
## ```
## To exclude them, use `After` and `Before`, like so:
##
## List.range { start: After 2, end: Before 5 } # returns [3, 4]
##
## ```
## List.range { start: After 2, end: Before 5 } # returns [3, 4]
## ```
## You can have the list end at a certain length rather than a certain integer:
##
## List.range { start: At 6, end: Length 4 } # returns [6, 7, 8, 9]
##
## ```
## List.range { start: At 6, end: Length 4 } # returns [6, 7, 8, 9]
## ```
## If `step` is specified, each integer increases by that much. (`step: 1` is the default.)
##
## List.range { start: After 0, end: Before 9, step: 3 } # returns [3, 6]
##
## ```
## List.range { start: After 0, end: Before 9, step: 3 } # returns [3, 6]
## ```
## List.range will also generate a reversed list if step is negative or end comes before start:
##
## List.range { start: At 5, end: At 2 } # returns [5, 4, 3, 2]
##
## ```
## List.range { start: At 5, end: At 2 } # returns [5, 4, 3, 2]
## ```
## All of these options are compatible with the others. For example, you can use `At` or `After`
## with `start` regardless of what `end` and `step` are set to.
range : _
@ -825,14 +831,14 @@ dropLast = \list ->
List.dropAt list (Num.subSaturated (List.len list) 1)
## Returns the given number of elements from the beginning of the list.
##
## List.takeFirst [1, 2, 3, 4, 5, 6, 7, 8] 4
##
## ```
## List.takeFirst [1, 2, 3, 4, 5, 6, 7, 8] 4
## ```
## If there are fewer elements in the list than the requested number,
## returns the entire list.
##
## List.takeFirst [1, 2] 5
##
## ```
## List.takeFirst [1, 2] 5
## ```
## To *remove* elements from the beginning of the list, use `List.takeLast`.
##
## To remove elements from both the beginning and end of the list,
@ -854,14 +860,14 @@ takeFirst = \list, outputLength ->
List.sublist list { start: 0, len: outputLength }
## Returns the given number of elements from the end of the list.
##
## List.takeLast [1, 2, 3, 4, 5, 6, 7, 8] 4
##
## ```
## List.takeLast [1, 2, 3, 4, 5, 6, 7, 8] 4
## ```
## If there are fewer elements in the list than the requested number,
## returns the entire list.
##
## List.takeLast [1, 2] 5
##
## ```
## List.takeLast [1, 2] 5
## ```
## To *remove* elements from the end of the list, use `List.takeFirst`.
##
## To remove elements from both the beginning and end of the list,
@ -1001,13 +1007,13 @@ findLastIndex = \list, matches ->
## including a total of `len` elements.
##
## If `start` is outside the bounds of the given list, returns the empty list.
##
## List.sublist [1, 2, 3] { start: 4, len: 0 }
##
## ```
## List.sublist [1, 2, 3] { start: 4, len: 0 }
## ```
## If more elements are requested than exist in the list, returns as many as it can.
##
## List.sublist [1, 2, 3, 4, 5] { start: 2, len: 10 }
##
## ```
## List.sublist [1, 2, 3, 4, 5] { start: 2, len: 10 }
## ```
## > If you want a sublist which goes all the way to the end of the list, no
## > matter how long the list is, `List.takeLast` can do that more efficiently.
##
@ -1023,7 +1029,7 @@ sublist = \list, config ->
sublistLowlevel : List elem, Nat, Nat -> List elem
## Intersperses `sep` between the elements of `list`
## List.intersperse 9 [1, 2, 3] # [1, 9, 2, 9, 3]
## List.intersperse 9 [1, 2, 3] # [1, 9, 2, 9, 3]
intersperse : List elem, elem -> List elem
intersperse = \list, sep ->
capacity = 2 * List.len list
@ -1081,8 +1087,9 @@ split = \elements, userSplitIndex ->
## Returns the elements before the first occurrence of a delimiter, as well as the
## remaining elements after that occurrence. If the delimiter is not found, returns `Err`.
##
## List.splitFirst [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo], after: [Bar, Baz] }
## ```
## List.splitFirst [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo], after: [Bar, Baz] }
## ```
splitFirst : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] | elem has Eq
splitFirst = \list, delimiter ->
when List.findFirstIndex list (\elem -> elem == delimiter) is
@ -1096,8 +1103,9 @@ splitFirst = \list, delimiter ->
## Returns the elements before the last occurrence of a delimiter, as well as the
## remaining elements after that occurrence. If the delimiter is not found, returns `Err`.
##
## List.splitLast [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo, Bar], after: [Baz] }
## ```
## List.splitLast [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo, Bar], after: [Baz] }
## ```
splitLast : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] | elem has Eq
splitLast = \list, delimiter ->
when List.findLastIndex list (\elem -> elem == delimiter) is

View file

@ -151,9 +151,9 @@ interface Num
## Represents a number that could be either an [Int] or a [Frac].
##
## This is useful for functions that can work on either, for example [Num.add], whose type is:
##
## add : Num a, Num a -> Num a
##
## ```
## add : Num a, Num a -> Num a
## ```
## The number 1.5 technically has the type `Num (Fraction *)`, so when you pass
## two of them to [Num.add], the answer you get is `3.0 : Num (Fraction *)`.
##
@ -191,9 +191,9 @@ interface Num
##
## If this default of [I64] is not big enough for your purposes,
## you can add an `i128` to the end of the number literal, like so:
##
## Num.toStr 5_000_000_000i128
##
## ```
## Num.toStr 5_000_000_000i128
## ```
## This `i128` suffix specifies that you want this number literal to be
## an [I128] instead of a `Num *`. All the other numeric types have
## suffixes just like `i128`; here are some other examples:
@ -259,15 +259,11 @@ Num range := range
##
## All number literals without decimal points are compatible with [Int] values.
##
## 1
##
## 0
##
## You can optionally put underscores in your [Int] literals.
## They have no effect on the number's value, but can make large numbers easier to read.
##
## 1_000_000
##
## ```
## 1_000_000
## ```
## Integers come in two flavors: *signed* and *unsigned*.
##
## * *Unsigned* integers can never be negative. The lowest value they can hold is zero.
@ -342,16 +338,16 @@ Int range : Num (Integer range)
##
## If you don't specify a type, Roc will default to using [Dec] because it's
## the least error-prone overall. For example, suppose you write this:
##
## wasItPrecise = 0.1 + 0.2 == 0.3
##
## ```
## wasItPrecise = 0.1 + 0.2 == 0.3
## ```
## The value of `wasItPrecise` here will be `Bool.true`, because Roc uses [Dec]
## by default when there are no types specified.
##
## In contrast, suppose we use `f32` or `f64` for one of these numbers:
##
## wasItPrecise = 0.1f64 + 0.2 == 0.3
##
## ```
## wasItPrecise = 0.1f64 + 0.2 == 0.3
## ```
## Here, `wasItPrecise` will be `Bool.false` because the entire calculation will have
## been done in a base-2 floating point calculation, which causes noticeable
## precision loss in this case.
@ -492,15 +488,14 @@ Dec : Num (FloatingPoint Decimal)
##
## This is the same as calling `Num.format {}` - so for more details on
## exact formatting, see `Num.format`.
##
## Num.toStr 42
##
## ```
## Num.toStr 42
## ```
## Only [Frac] values will include a decimal point, and they will always include one.
##
## Num.toStr 4.2
##
## Num.toStr 4.0
##
## ```
## Num.toStr 4.2
## Num.toStr 4.0
## ```
## When this function is given a non-[finite](Num.isFinite)
## [F64] or [F32] value, the returned string will be `"NaN"`, `"∞"`, or `"-∞"`.
##
@ -539,9 +534,10 @@ compare : Num a, Num a -> [LT, EQ, GT]
##
## If either argument is [*NaN*](Num.isNaN), returns `Bool.false` no matter what. (*NaN*
## is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).)
##
## 5
## |> Num.isLt 6
## ```
## 5
## |> Num.isLt 6
## ```
isLt : Num a, Num a -> Bool
## Returns `Bool.true` if the first number is greater than the second.
@ -550,9 +546,10 @@ isLt : Num a, Num a -> Bool
##
## If either argument is [*NaN*](Num.isNaN), returns `Bool.false` no matter what. (*NaN*
## is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).)
##
## 6
## |> Num.isGt 5
## ```
## 6
## |> Num.isGt 5
## ```
isGt : Num a, Num a -> Bool
## Returns `Bool.true` if the first number is less than or equal to the second.
@ -601,15 +598,15 @@ toFrac : Num * -> Frac *
## * For a positive number, returns the same number.
## * For a negative number, returns the same number except positive.
## * For zero, returns zero.
## ```
## Num.abs 4
##
## Num.abs 4
## Num.abs -2.5
##
## Num.abs -2.5
##
## Num.abs 0
##
## Num.abs 0.0
## Num.abs 0
##
## Num.abs 0.0
## ```
## This is safe to use with any [Frac], but it can cause overflow when used with certain [Int] values.
##
## For example, calling #Num.abs on the lowest value of a signed integer (such as [Num.minI64] or [Num.minI32]) will cause overflow.
@ -620,15 +617,15 @@ toFrac : Num * -> Frac *
abs : Num a -> Num a
## Return a negative number when given a positive one, and vice versa.
## ```
## Num.neg 5
##
## Num.neg 5
## Num.neg -2.5
##
## Num.neg -2.5
##
## Num.neg 0
##
## Num.neg 0.0
## Num.neg 0
##
## Num.neg 0.0
## ```
## This is safe to use with any [Frac], but it can cause overflow when used with certain [Int] values.
##
## For example, calling #Num.neg on the lowest value of a signed integer (such as [Num.minI64] or [Num.minI32]) will cause overflow.
@ -645,16 +642,16 @@ neg : Num a -> Num a
## (To add an [Int] and a [Frac], first convert one so that they both have the same type. There are functions in this module that can convert both [Int] to [Frac] and the other way around.)
##
## `a + b` is shorthand for `Num.add a b`.
## ```
## 5 + 7
##
## 5 + 7
##
## Num.add 5 7
##
## Num.add 5 7
## ```
## `Num.add` can be convenient in pipelines.
##
## Frac.pi
## |> Num.add 1.0
##
## ```
## Frac.pi
## |> Num.add 1.0
## ```
## If the answer to this operation can't fit in the return value (e.g. an
## [I8] answer that's higher than 127 or lower than -128), the result is an
## *overflow*. For [F64] and [F32], overflow results in an answer of either
@ -666,16 +663,16 @@ add : Num a, Num a -> Num a
## (To subtract an [Int] and a [Frac], first convert one so that they both have the same type. There are functions in this module that can convert both [Int] to [Frac] and the other way around.)
##
## `a - b` is shorthand for `Num.sub a b`.
## ```
## 7 - 5
##
## 7 - 5
##
## Num.sub 7 5
##
## Num.sub 7 5
## ```
## `Num.sub` can be convenient in pipelines.
##
## Frac.pi
## |> Num.sub 2.0
##
## ```
## Frac.pi
## |> Num.sub 2.0
## ```
## If the answer to this operation can't fit in the return value (e.g. an
## [I8] answer that's higher than 127 or lower than -128), the result is an
## *overflow*. For [F64] and [F32], overflow results in an answer of either
@ -687,16 +684,18 @@ sub : Num a, Num a -> Num a
## (To multiply an [Int] and a [Frac], first convert one so that they both have the same type. There are functions in this module that can convert both [Int] to [Frac] and the other way around.)
##
## `a * b` is shorthand for `Num.mul a b`.
## ```
## 5 * 7
##
## 5 * 7
##
## Num.mul 5 7
## Num.mul 5 7
## ```
##
## `Num.mul` can be convenient in pipelines.
##
## Frac.pi
## |> Num.mul 2.0
##
## ```
## Frac.pi
## |> Num.mul 2.0
## ```
## If the answer to this operation can't fit in the return value (e.g. an
## [I8] answer that's higher than 127 or lower than -128), the result is an
## *overflow*. For [F64] and [F32], overflow results in an answer of either
@ -731,14 +730,15 @@ atan : Frac a -> Frac a
## > this standard, deviating from these rules has a significant performance
## > cost! Since the most common reason to choose [F64] or [F32] over [Dec] is
## > access to hardware-accelerated performance, Roc follows these rules exactly.
## ```
## Num.sqrt 4.0
##
## Num.sqrt 4.0
## Num.sqrt 1.5
##
## Num.sqrt 1.5
## Num.sqrt 0.0
##
## Num.sqrt 0.0
##
## Num.sqrt -4.0f64
## Num.sqrt -4.0f64
## ```
sqrt : Frac a -> Frac a
sqrtChecked : Frac a -> Result (Frac a) [SqrtOfNegative]
@ -779,15 +779,16 @@ logChecked = \x ->
##
## To divide an [Int] and a [Frac], first convert the [Int] to a [Frac] using
## one of the functions in this module like #toDec.
## ```
## 5.0 / 7.0
##
## 5.0 / 7.0
##
## Num.div 5 7
##
## Num.div 5 7
## ```
## `Num.div` can be convenient in pipelines.
##
## Num.pi
## |> Num.div 2.0
## ```
## Num.pi
## |> Num.div 2.0
## ```
div : Frac a, Frac a -> Frac a
divChecked : Frac a, Frac a -> Result (Frac a) [DivByZero]
@ -813,15 +814,15 @@ divCeilChecked = \a, b ->
## Division by zero is undefined in mathematics. As such, you should make
## sure never to pass zero as the denomaintor to this function! If you do,
## it will crash.
## ```
## 5 // 7
##
## 5 // 7
## Num.divTrunc 5 7
##
## Num.divTrunc 5 7
##
## 8 // -3
##
## Num.divTrunc 8 -3
## 8 // -3
##
## Num.divTrunc 8 -3
## ```
divTrunc : Int a, Int a -> Int a
divTruncChecked : Int a, Int a -> Result (Int a) [DivByZero]
@ -834,14 +835,15 @@ divTruncChecked = \a, b ->
## Obtain the remainder (truncating modulo) from the division of two integers.
##
## `a % b` is shorthand for `Num.rem a b`.
## ```
## 5 % 7
##
## 5 % 7
## Num.rem 5 7
##
## Num.rem 5 7
## -8 % -3
##
## -8 % -3
##
## Num.rem -8 -3
## Num.rem -8 -3
## ```
rem : Int a, Int a -> Int a
remChecked : Int a, Int a -> Result (Int a) [DivByZero]
@ -861,24 +863,24 @@ bitwiseOr : Int a, Int a -> Int a
##
## The least significant bits always become 0. This means that shifting left is
## like multiplying by factors of two for unsigned integers.
## ```
## shiftLeftBy 0b0000_0011 2 == 0b0000_1100
##
## shiftLeftBy 0b0000_0011 2 == 0b0000_1100
##
## 0b0000_0101 |> shiftLeftBy 2 == 0b0000_1100
##
## 0b0000_0101 |> shiftLeftBy 2 == 0b0000_1100
## ```
## In some languages `shiftLeftBy` is implemented as a binary operator `<<`.
shiftLeftBy : Int a, U8 -> Int a
## Bitwise arithmetic shift of a number by another
##
## The most significant bits are copied from the current.
## ```
## shiftRightBy 0b0000_0011 2 == 0b0000_1100
##
## shiftRightBy 0b0000_0011 2 == 0b0000_1100
##
## 0b0001_0100 |> shiftRightBy 2 == 0b0000_0101
##
## 0b1001_0000 |> shiftRightBy 2 == 0b1110_0100
## 0b0001_0100 |> shiftRightBy 2 == 0b0000_0101
##
## 0b1001_0000 |> shiftRightBy 2 == 0b1110_0100
## ```
## In some languages `shiftRightBy` is implemented as a binary operator `>>>`.
shiftRightBy : Int a, U8 -> Int a
@ -886,13 +888,13 @@ shiftRightBy : Int a, U8 -> Int a
##
## The most significant bits always become 0. This means that shifting left is
## like dividing by factors of two for unsigned integers.
## ```
## shiftRightBy 0b0010_1000 2 == 0b0000_1010
##
## shiftRightBy 0b0010_1000 2 == 0b0000_1010
##
## 0b0010_1000 |> shiftRightBy 2 == 0b0000_1010
##
## 0b1001_0000 |> shiftRightBy 2 == 0b0010_0100
## 0b0010_1000 |> shiftRightBy 2 == 0b0000_1010
##
## 0b1001_0000 |> shiftRightBy 2 == 0b0010_0100
## ```
## In some languages `shiftRightBy` is implemented as a binary operator `>>`.
shiftRightZfBy : Int a, U8 -> Int a
@ -913,15 +915,15 @@ pow : Frac a, Frac a -> Frac a
##
## For a [Frac] alternative to this function, which supports negative exponents,
## see #Num.exp.
## ```
## Num.exp 5 0
##
## Num.exp 5 0
## Num.exp 5 1
##
## Num.exp 5 1
##
## Num.exp 5 2
##
## Num.exp 5 6
## Num.exp 5 2
##
## Num.exp 5 6
## ```
## ## Performance Notes
##
## Be careful! It is very easy for this function to produce an answer
@ -1284,11 +1286,11 @@ toF64Checked : Num * -> Result F64 [OutOfBounds]
## *NaN* ([not a number](https://en.wikipedia.org/wiki/NaN)), and `Bool.false` otherwise.
##
## Always returns `Bool.false` when given a [Dec].
## ```
## Num.isNaN 12.3
##
## Num.isNaN 12.3
##
## Num.isNaN (Num.pow -1 0.5)
##
## Num.isNaN (Num.pow -1 0.5)
## ```
## *NaN* is unusual from other numberic values in that:
## * *NaN* is not equal to any other number, even itself. [Bool.isEq] always returns `Bool.false` if either argument is *NaN*.
## * *NaN* has no ordering, so [isLt], [isLte], [isGt], and [isGte] always return `Bool.false` if either argument is *NaN*.

View file

@ -7,8 +7,9 @@ interface Result
Result ok err : [Ok ok, Err err]
## Return `Bool.true` if the result indicates a success, else return `Bool.false`
##
## Result.isOk (Ok 5)
## ```
## Result.isOk (Ok 5)
## ```
isOk : Result ok err -> Bool
isOk = \result ->
when result is
@ -16,8 +17,9 @@ isOk = \result ->
Err _ -> Bool.false
## Return `Bool.true` if the result indicates a failure, else return `Bool.false`
##
## Result.isErr (Err "uh oh")
## ```
## Result.isErr (Err "uh oh")
## ```
isErr : Result ok err -> Bool
isErr = \result ->
when result is
@ -26,10 +28,10 @@ isErr = \result ->
## If the result is `Ok`, return the value it holds. Otherwise, return
## the given default value.
##
## Result.withDefault (Ok 7) 42
##
## Result.withDefault (Err "uh oh") 42
## ```
## Result.withDefault (Ok 7) 42
## Result.withDefault (Err "uh oh") 42
## ```
withDefault : Result ok err, ok -> ok
withDefault = \result, default ->
when result is
@ -40,10 +42,10 @@ withDefault = \result, default ->
## function on it. Then return a new `Ok` holding the transformed value.
##
## (If the result is `Err`, this has no effect. Use [mapErr] to transform an `Err`.)
##
## Result.map (Ok 12) Num.negate
##
## Result.map (Err "yipes!") Num.negate
## ```
## Result.map (Ok 12) Num.negate
## Result.map (Err "yipes!") Num.negate
## ```
##
## `map` functions like this are common in Roc, and they all work similarly.
## See for example [List.map], `Set.map`, and `Dict.map`.
@ -57,10 +59,10 @@ map = \result, transform ->
## function on it. Then return a new `Err` holding the transformed value.
##
## (If the result is `Ok`, this has no effect. Use [map] to transform an `Ok`.)
##
## Result.mapErr (Err "yipes!") Str.isEmpty
##
## Result.mapErr (Ok 12) Str.isEmpty
## ```
## Result.mapErr (Err "yipes!") Str.isEmpty
## Result.mapErr (Ok 12) Str.isEmpty
## ```
mapErr : Result ok a, (a -> b) -> Result ok b
mapErr = \result, transform ->
when result is
@ -71,10 +73,10 @@ mapErr = \result, transform ->
## function on the value the `Ok` holds. Then return that new result.
##
## (If the result is `Err`, this has no effect. Use `onErr` to transform an `Err`.)
##
## Result.try (Ok -1) \num -> if num < 0 then Err "negative!" else Ok -num
##
## Result.try (Err "yipes!") \num -> if num < 0 then Err "negative!" else Ok -num
## ```
## Result.try (Ok -1) \num -> if num < 0 then Err "negative!" else Ok -num
## Result.try (Err "yipes!") \num -> if num < 0 then Err "negative!" else Ok -num
## ```
try : Result a err, (a -> Result b err) -> Result b err
try = \result, transform ->
when result is
@ -85,10 +87,10 @@ try = \result, transform ->
## function on the value the `Err` holds. Then return that new result.
##
## (If the result is `Ok`, this has no effect. Use `try` to transform an `Ok`.)
##
## Result.onErr (Ok 10) \errorNum -> Str.toNat errorNum
##
## Result.onErr (Err "42") \errorNum -> Str.toNat errorNum
## ```
## Result.onErr (Ok 10) \errorNum -> Str.toNat errorNum
## Result.onErr (Err "42") \errorNum -> Str.toNat errorNum
## ```
onErr : Result a err, (err -> Result a otherErr) -> Result a otherErr
onErr = \result, transform ->
when result is

View file

@ -4,11 +4,11 @@
##
## Unicode can represent text values which span multiple languages, symbols, and emoji.
## Here are some valid Roc strings:
##
## "Roc!"
## "鹏"
## "🕊"
##
## ```
## "Roc!"
## "鹏"
## "🕊"
## ```
## Every Unicode string is a sequence of [extended grapheme clusters](http://www.unicode.org/glossary/#extended_grapheme_cluster).
## An extended grapheme cluster represents what a person reading a string might
## call a "character" - like "A" or "ö" or "👩‍👩‍👦‍👦".
@ -17,11 +17,11 @@
## term "grapheme" as a shorthand for the more precise "extended grapheme cluster."
##
## You can get the number of graphemes in a string by calling `Str.countGraphemes` on it:
##
## Str.countGraphemes "Roc!"
## Str.countGraphemes "折り紙"
## Str.countGraphemes "🕊"
##
## ```
## Str.countGraphemes "Roc!"
## Str.countGraphemes "折り紙"
## Str.countGraphemes "🕊"
## ```
## > The `countGraphemes` function walks through the entire string to get its answer,
## > so if you want to check whether a string is empty, you'll get much better performance
## > by calling `Str.isEmpty myStr` instead of `Str.countGraphemes myStr == 0`.
@ -31,9 +31,9 @@
## If you put a `\` in a Roc string literal, it begins an *escape sequence*.
## An escape sequence is a convenient way to insert certain strings into other strings.
## For example, suppose you write this Roc string:
##
## "I took the one less traveled by,\nAnd that has made all the difference."
##
## ```
## "I took the one less traveled by,\nAnd that has made all the difference."
## ```
## The `"\n"` in the middle will insert a line break into this string. There are
## other ways of getting a line break in there, but `"\n"` is the most common.
##
@ -58,12 +58,11 @@
## * `\v` - [vertical tab](https://en.wikipedia.org/wiki/Tab_key#Tab_characters)
##
## You can also use escape sequences to insert named strings into other strings, like so:
##
## name = "Lee"
## city = "Roctown"
##
## greeting = "Hello there, \(name)! Welcome to \(city)."
##
## ```
## name = "Lee"
## city = "Roctown"
## greeting = "Hello there, \(name)! Welcome to \(city)."
## ```
## Here, `greeting` will become the string `"Hello there, Lee! Welcome to Roctown."`.
## This is known as [string interpolation](https://en.wikipedia.org/wiki/String_interpolation),
## and you can use it as many times as you like inside a string. The name
@ -138,16 +137,18 @@ Utf8ByteProblem : [
Utf8Problem : { byteIndex : Nat, problem : Utf8ByteProblem }
## Returns [Bool.true] if the string is empty, and [Bool.false] otherwise.
##
## expect Str.isEmpty "hi!" == Bool.false
## expect Str.isEmpty "" == Bool.true
## ```
## expect Str.isEmpty "hi!" == Bool.false
## expect Str.isEmpty "" == Bool.true
## ```
isEmpty : Str -> Bool
## Concatenates two strings together.
##
## expect Str.concat "ab" "cd" == "abcd"
## expect Str.concat "hello" "" == "hello"
## expect Str.concat "" "" == ""
## ```
## expect Str.concat "ab" "cd" == "abcd"
## expect Str.concat "hello" "" == "hello"
## expect Str.concat "" "" == ""
## ```
concat : Str, Str -> Str
## Returns a string of the specified capacity without any content.
@ -155,9 +156,10 @@ withCapacity : Nat -> Str
## Combines a [List] of strings into a single string, with a separator
## string in between each.
##
## expect Str.joinWith ["one", "two", "three"] ", " == "one, two, three"
## expect Str.joinWith ["1", "2", "3", "4"] "." == "1.2.3.4"
## ```
## expect Str.joinWith ["one", "two", "three"] ", " == "one, two, three"
## expect Str.joinWith ["1", "2", "3", "4"] "." == "1.2.3.4"
## ```
joinWith : List Str, Str -> Str
## Split a string around a separator.
@ -165,20 +167,22 @@ joinWith : List Str, Str -> Str
## Passing `""` for the separator is not useful;
## it returns the original string wrapped in a [List]. To split a string
## into its individual [graphemes](https://stackoverflow.com/a/27331885/4200103), use `Str.graphemes`
##
## expect Str.split "1,2,3" "," == ["1","2","3"]
## expect Str.split "1,2,3" "" == ["1,2,3"]
## ```
## expect Str.split "1,2,3" "," == ["1","2","3"]
## expect Str.split "1,2,3" "" == ["1,2,3"]
## ```
split : Str, Str -> List Str
## Repeats a string the given number of times.
##
## expect Str.repeat "z" 3 == "zzz"
## expect Str.repeat "na" 8 == "nananananananana"
##
## ```
## expect Str.repeat "z" 3 == "zzz"
## expect Str.repeat "na" 8 == "nananananananana"
## ```
## Returns `""` when given `""` for the string or `0` for the count.
##
## expect Str.repeat "" 10 == ""
## expect Str.repeat "anything" 0 == ""
## ```
## expect Str.repeat "" 10 == ""
## expect Str.repeat "anything" 0 == ""
## ```
repeat : Str, Nat -> Str
## Counts the number of [extended grapheme clusters](http://www.unicode.org/glossary/#extended_grapheme_cluster)
@ -186,11 +190,11 @@ repeat : Str, Nat -> Str
##
## Note that the number of extended grapheme clusters can be different from the number
## of visual glyphs rendered! Consider the following examples:
##
## expect Str.countGraphemes "Roc" == 3
## expect Str.countGraphemes "👩‍👩‍👦‍👦" == 4
## expect Str.countGraphemes "🕊" == 1
##
## ```
## expect Str.countGraphemes "Roc" == 3
## expect Str.countGraphemes "👩‍👩‍👦‍👦" == 4
## expect Str.countGraphemes "🕊" == 1
## ```
## Note that "👩‍👩‍👦‍👦" takes up 4 graphemes (even though visually it appears as a single
## glyph) because under the hood it's represented using an emoji modifier sequence.
## In contrast, "🕊" only takes up 1 grapheme because under the hood it's represented
@ -205,11 +209,11 @@ graphemes : Str -> List Str
##
## If the given string is empty, or if the given [U32] is not a valid
## code point, returns [Bool.false].
##
## expect Str.startsWithScalar "鹏 means 'roc'" 40527 # "鹏" is Unicode scalar 40527
## expect !Str.startsWithScalar "9" 9 # the Unicode scalar for "9" is 57, not 9
## expect !Str.startsWithScalar "" 40527
##
## ```
## expect Str.startsWithScalar "鹏 means 'roc'" 40527 # "鹏" is Unicode scalar 40527
## expect !Str.startsWithScalar "9" 9 # the Unicode scalar for "9" is 57, not 9
## expect !Str.startsWithScalar "" 40527
## ```
## **Performance Note:** This runs slightly faster than [Str.startsWith], so
## if you want to check whether a string begins with something that's representable
## in a single code point, you can use (for example) `Str.startsWithScalar '鹏'`
@ -225,36 +229,39 @@ startsWithScalar : Str, U32 -> Bool
##
## (Roc strings contain only scalar values, not [surrogate code points](https://unicode.org/glossary/#surrogate_code_point),
## so this is equivalent to returning a list of the string's [code points](https://unicode.org/glossary/#code_point).)
##
## expect Str.toScalars "Roc" == [82, 111, 99]
## expect Str.toScalars "鹏" == [40527]
## expect Str.toScalars "சி" == [2970, 3007]
## expect Str.toScalars "🐦" == [128038]
## expect Str.toScalars "👩‍👩‍👦‍👦" == [128105, 8205, 128105, 8205, 128102, 8205, 128102]
## expect Str.toScalars "I ♥ Roc" == [73, 32, 9829, 32, 82, 111, 99]
## expect Str.toScalars "" == []
## ```
## expect Str.toScalars "Roc" == [82, 111, 99]
## expect Str.toScalars "鹏" == [40527]
## expect Str.toScalars "சி" == [2970, 3007]
## expect Str.toScalars "🐦" == [128038]
## expect Str.toScalars "👩‍👩‍👦‍👦" == [128105, 8205, 128105, 8205, 128102, 8205, 128102]
## expect Str.toScalars "I ♥ Roc" == [73, 32, 9829, 32, 82, 111, 99]
## expect Str.toScalars "" == []
## ```
toScalars : Str -> List U32
## Returns a [List] of the string's [U8] UTF-8 [code units](https://unicode.org/glossary/#code_unit).
## (To split the string into a [List] of smaller [Str] values instead of [U8] values,
## see [Str.split].)
##
## expect Str.toUtf8 "Roc" == [82, 111, 99]
## expect Str.toUtf8 "鹏" == [233, 185, 143]
## expect Str.toUtf8 "சி" == [224, 174, 154, 224, 174, 191]
## expect Str.toUtf8 "🐦" == [240, 159, 144, 166]
## ```
## expect Str.toUtf8 "Roc" == [82, 111, 99]
## expect Str.toUtf8 "鹏" == [233, 185, 143]
## expect Str.toUtf8 "சி" == [224, 174, 154, 224, 174, 191]
## expect Str.toUtf8 "🐦" == [240, 159, 144, 166]
## ```
toUtf8 : Str -> List U8
## Converts a [List] of [U8] UTF-8 [code units](https://unicode.org/glossary/#code_unit) to a string.
##
## Returns `Err` if the given bytes are invalid UTF-8, and returns `Ok ""` when given `[]`.
##
## expect Str.fromUtf8 [82, 111, 99] == Ok "Roc"
## expect Str.fromUtf8 [233, 185, 143] == Ok "鹏"
## expect Str.fromUtf8 [224, 174, 154, 224, 174, 191] == Ok "சி"
## expect Str.fromUtf8 [240, 159, 144, 166] == Ok "🐦"
## expect Str.fromUtf8 [] == Ok ""
## expect Str.fromUtf8 [255] |> Result.isErr
## ```
## expect Str.fromUtf8 [82, 111, 99] == Ok "Roc"
## expect Str.fromUtf8 [233, 185, 143] == Ok "鹏"
## expect Str.fromUtf8 [224, 174, 154, 224, 174, 191] == Ok "சி"
## expect Str.fromUtf8 [240, 159, 144, 166] == Ok "🐦"
## expect Str.fromUtf8 [] == Ok ""
## expect Str.fromUtf8 [255] |> Result.isErr
## ```
fromUtf8 : List U8 -> Result Str [BadUtf8 Utf8ByteProblem Nat]
fromUtf8 = \bytes ->
result = fromUtf8RangeLowlevel bytes 0 (List.len bytes)
@ -266,8 +273,9 @@ fromUtf8 = \bytes ->
## Encode part of a [List] of [U8] UTF-8 [code units](https://unicode.org/glossary/#code_unit)
## into a [Str]
##
## expect Str.fromUtf8Range [72, 105, 80, 103] { start : 0, count : 2 } == Ok "Hi"
## ```
## expect Str.fromUtf8Range [72, 105, 80, 103] { start : 0, count : 2 } == Ok "Hi"
## ```
fromUtf8Range : List U8, { start : Nat, count : Nat } -> Result Str [BadUtf8 Utf8ByteProblem Nat, OutOfBounds]
fromUtf8Range = \bytes, config ->
if config.start + config.count <= List.len bytes then
@ -290,57 +298,65 @@ FromUtf8Result : {
fromUtf8RangeLowlevel : List U8, Nat, Nat -> FromUtf8Result
## Check if the given [Str] starts with a value.
##
## expect Str.startsWith "ABC" "A" == Bool.true
## expect Str.startsWith "ABC" "X" == Bool.false
## ```
## expect Str.startsWith "ABC" "A" == Bool.true
## expect Str.startsWith "ABC" "X" == Bool.false
## ```
startsWith : Str, Str -> Bool
## Check if the given [Str] ends with a value.
##
## expect Str.endsWith "ABC" "C" == Bool.true
## expect Str.endsWith "ABC" "X" == Bool.false
## ```
## expect Str.endsWith "ABC" "C" == Bool.true
## expect Str.endsWith "ABC" "X" == Bool.false
## ```
endsWith : Str, Str -> Bool
## Return the [Str] with all whitespace removed from both the beginning
## as well as the end.
##
## expect Str.trim " Hello \n\n" == "Hello"
## ```
## expect Str.trim " Hello \n\n" == "Hello"
## ```
trim : Str -> Str
## Return the [Str] with all whitespace removed from the beginning.
##
## expect Str.trimLeft " Hello \n\n" == "Hello \n\n"
## ```
## expect Str.trimLeft " Hello \n\n" == "Hello \n\n"
## ```
trimLeft : Str -> Str
## Return the [Str] with all whitespace removed from the end.
##
## expect Str.trimRight " Hello \n\n" == " Hello"
## ```
## expect Str.trimRight " Hello \n\n" == " Hello"
## ```
trimRight : Str -> Str
## Encode a [Str] to a [Dec]. A [Dec] value is a 128-bit decimal
## [fixed-point number](https://en.wikipedia.org/wiki/Fixed-point_arithmetic).
##
## expect Str.toDec "10" == Ok 10dec
## expect Str.toDec "-0.25" == Ok -0.25dec
## expect Str.toDec "not a number" == Err InvalidNumStr
## ```
## expect Str.toDec "10" == Ok 10dec
## expect Str.toDec "-0.25" == Ok -0.25dec
## expect Str.toDec "not a number" == Err InvalidNumStr
## ```
toDec : Str -> Result Dec [InvalidNumStr]
toDec = \string -> strToNumHelp string
## Encode a [Str] to a [F64]. A [F64] value is a 64-bit
## [floating-point number](https://en.wikipedia.org/wiki/IEEE_754) and can be
## specified with a `f64` suffix.
##
## expect Str.toF64 "0.10" == Ok 0.10f64
## expect Str.toF64 "not a number" == Err InvalidNumStr
## ```
## expect Str.toF64 "0.10" == Ok 0.10f64
## expect Str.toF64 "not a number" == Err InvalidNumStr
## ```
toF64 : Str -> Result F64 [InvalidNumStr]
toF64 = \string -> strToNumHelp string
## Encode a [Str] to a [F32].A [F32] value is a 32-bit
## [floating-point number](https://en.wikipedia.org/wiki/IEEE_754) and can be
## specified with a `f32` suffix.
##
## expect Str.toF32 "0.10" == Ok 0.10f32
## expect Str.toF32 "not a number" == Err InvalidNumStr
## ```
## expect Str.toF32 "0.10" == Ok 0.10f32
## expect Str.toF32 "not a number" == Err InvalidNumStr
## ```
toF32 : Str -> Result F32 [InvalidNumStr]
toF32 = \string -> strToNumHelp string
@ -356,20 +372,22 @@ toF32 = \string -> strToNumHelp string
## Calling `Str.toNat "9_000_000_000"` on a 64-bit system will return
## the [Nat] value of 9_000_000_000. This is because on a 64-bit system, [Nat] can
## hold up to `Num.maxU64`, and 9_000_000_000 is smaller than `Num.maxU64`.
##
## expect Str.toNat "9_000_000_000" == Ok 9000000000
## expect Str.toNat "not a number" == Err InvalidNumStr
## ```
## expect Str.toNat "9_000_000_000" == Ok 9000000000
## expect Str.toNat "not a number" == Err InvalidNumStr
## ```
toNat : Str -> Result Nat [InvalidNumStr]
toNat = \string -> strToNumHelp string
## Encode a [Str] to an unsigned [U128] integer. A [U128] value can hold numbers
## from `0` to `340_282_366_920_938_463_463_374_607_431_768_211_455` (over
## 340 undecillion). It can be specified with a u128 suffix.
##
## expect Str.toU128 "1500" == Ok 1500u128
## expect Str.toU128 "0.1" == Err InvalidNumStr
## expect Str.toU128 "-1" == Err InvalidNumStr
## expect Str.toU128 "not a number" == Err InvalidNumStr
## ```
## expect Str.toU128 "1500" == Ok 1500u128
## expect Str.toU128 "0.1" == Err InvalidNumStr
## expect Str.toU128 "-1" == Err InvalidNumStr
## expect Str.toU128 "not a number" == Err InvalidNumStr
## ```
toU128 : Str -> Result U128 [InvalidNumStr]
toU128 = \string -> strToNumHelp string
@ -377,96 +395,105 @@ toU128 = \string -> strToNumHelp string
## from `-170_141_183_460_469_231_731_687_303_715_884_105_728` to
## `170_141_183_460_469_231_731_687_303_715_884_105_727`. It can be specified
## with a i128 suffix.
##
## expect Str.toI128 "1500" == Ok 1500i128
## expect Str.toI128 "-1" == Ok -1i128
## expect Str.toI128 "0.1" == Err InvalidNumStr
## expect Str.toI128 "not a number" == Err InvalidNumStr
## ```
## expect Str.toI128 "1500" == Ok 1500i128
## expect Str.toI128 "-1" == Ok -1i128
## expect Str.toI128 "0.1" == Err InvalidNumStr
## expect Str.toI128 "not a number" == Err InvalidNumStr
## ```
toI128 : Str -> Result I128 [InvalidNumStr]
toI128 = \string -> strToNumHelp string
## Encode a [Str] to an unsigned [U64] integer. A [U64] value can hold numbers
## from `0` to `18_446_744_073_709_551_615` (over 18 quintillion). It
## can be specified with a u64 suffix.
##
## expect Str.toU64 "1500" == Ok 1500u64
## expect Str.toU64 "0.1" == Err InvalidNumStr
## expect Str.toU64 "-1" == Err InvalidNumStr
## expect Str.toU64 "not a number" == Err InvalidNumStr
## ```
## expect Str.toU64 "1500" == Ok 1500u64
## expect Str.toU64 "0.1" == Err InvalidNumStr
## expect Str.toU64 "-1" == Err InvalidNumStr
## expect Str.toU64 "not a number" == Err InvalidNumStr
## ```
toU64 : Str -> Result U64 [InvalidNumStr]
toU64 = \string -> strToNumHelp string
## Encode a [Str] to a signed [I64] integer. A [I64] value can hold numbers
## from `-9_223_372_036_854_775_808` to `9_223_372_036_854_775_807`. It can be
## specified with a i64 suffix.
##
## expect Str.toI64 "1500" == Ok 1500i64
## expect Str.toI64 "-1" == Ok -1i64
## expect Str.toI64 "0.1" == Err InvalidNumStr
## expect Str.toI64 "not a number" == Err InvalidNumStr
## ```
## expect Str.toI64 "1500" == Ok 1500i64
## expect Str.toI64 "-1" == Ok -1i64
## expect Str.toI64 "0.1" == Err InvalidNumStr
## expect Str.toI64 "not a number" == Err InvalidNumStr
## ```
toI64 : Str -> Result I64 [InvalidNumStr]
toI64 = \string -> strToNumHelp string
## Encode a [Str] to an unsigned [U32] integer. A [U32] value can hold numbers
## from `0` to `4_294_967_295` (over 4 billion). It can be specified with
## a u32 suffix.
##
## expect Str.toU32 "1500" == Ok 1500u32
## expect Str.toU32 "0.1" == Err InvalidNumStr
## expect Str.toU32 "-1" == Err InvalidNumStr
## expect Str.toU32 "not a number" == Err InvalidNumStr
## ```
## expect Str.toU32 "1500" == Ok 1500u32
## expect Str.toU32 "0.1" == Err InvalidNumStr
## expect Str.toU32 "-1" == Err InvalidNumStr
## expect Str.toU32 "not a number" == Err InvalidNumStr
## ```
toU32 : Str -> Result U32 [InvalidNumStr]
toU32 = \string -> strToNumHelp string
## Encode a [Str] to a signed [I32] integer. A [I32] value can hold numbers
## from `-2_147_483_648` to `2_147_483_647`. It can be
## specified with a i32 suffix.
##
## expect Str.toI32 "1500" == Ok 1500i32
## expect Str.toI32 "-1" == Ok -1i32
## expect Str.toI32 "0.1" == Err InvalidNumStr
## expect Str.toI32 "not a number" == Err InvalidNumStr
## ```
## expect Str.toI32 "1500" == Ok 1500i32
## expect Str.toI32 "-1" == Ok -1i32
## expect Str.toI32 "0.1" == Err InvalidNumStr
## expect Str.toI32 "not a number" == Err InvalidNumStr
## ```
toI32 : Str -> Result I32 [InvalidNumStr]
toI32 = \string -> strToNumHelp string
## Encode a [Str] to an unsigned [U16] integer. A [U16] value can hold numbers
## from `0` to `65_535`. It can be specified with a u16 suffix.
##
## expect Str.toU16 "1500" == Ok 1500u16
## expect Str.toU16 "0.1" == Err InvalidNumStr
## expect Str.toU16 "-1" == Err InvalidNumStr
## expect Str.toU16 "not a number" == Err InvalidNumStr
## ```
## expect Str.toU16 "1500" == Ok 1500u16
## expect Str.toU16 "0.1" == Err InvalidNumStr
## expect Str.toU16 "-1" == Err InvalidNumStr
## expect Str.toU16 "not a number" == Err InvalidNumStr
## ```
toU16 : Str -> Result U16 [InvalidNumStr]
toU16 = \string -> strToNumHelp string
## Encode a [Str] to a signed [I16] integer. A [I16] value can hold numbers
## from `-32_768` to `32_767`. It can be
## specified with a i16 suffix.
##
## expect Str.toI16 "1500" == Ok 1500i16
## expect Str.toI16 "-1" == Ok -1i16
## expect Str.toI16 "0.1" == Err InvalidNumStr
## expect Str.toI16 "not a number" == Err InvalidNumStr
## ```
## expect Str.toI16 "1500" == Ok 1500i16
## expect Str.toI16 "-1" == Ok -1i16
## expect Str.toI16 "0.1" == Err InvalidNumStr
## expect Str.toI16 "not a number" == Err InvalidNumStr
## ```
toI16 : Str -> Result I16 [InvalidNumStr]
toI16 = \string -> strToNumHelp string
## Encode a [Str] to an unsigned [U8] integer. A [U8] value can hold numbers
## from `0` to `255`. It can be specified with a u8 suffix.
##
## expect Str.toU8 "250" == Ok 250u8
## expect Str.toU8 "-0.1" == Err InvalidNumStr
## expect Str.toU8 "not a number" == Err InvalidNumStr
## expect Str.toU8 "1500" == Err InvalidNumStr
## ```
## expect Str.toU8 "250" == Ok 250u8
## expect Str.toU8 "-0.1" == Err InvalidNumStr
## expect Str.toU8 "not a number" == Err InvalidNumStr
## expect Str.toU8 "1500" == Err InvalidNumStr
## ```
toU8 : Str -> Result U8 [InvalidNumStr]
toU8 = \string -> strToNumHelp string
## Encode a [Str] to a signed [I8] integer. A [I8] value can hold numbers
## from `-128` to `127`. It can be
## specified with a i8 suffix.
##
## expect Str.toI8 "-15" == Ok -15i8
## expect Str.toI8 "150.00" == Err InvalidNumStr
## expect Str.toI8 "not a number" == Err InvalidNumStr
## ```
## expect Str.toI8 "-15" == Ok -15i8
## expect Str.toI8 "150.00" == Err InvalidNumStr
## expect Str.toI8 "not a number" == Err InvalidNumStr
## ```
toI8 : Str -> Result I8 [InvalidNumStr]
toI8 = \string -> strToNumHelp string
@ -474,8 +501,9 @@ toI8 = \string -> strToNumHelp string
getUnsafe : Str, Nat -> U8
## Gives the number of bytes in a [Str] value.
##
## expect Str.countUtf8Bytes "Hello World" == 11
## ```
## expect Str.countUtf8Bytes "Hello World" == 11
## ```
countUtf8Bytes : Str -> Nat
## string slice that does not do bounds checking or utf-8 verification
@ -483,9 +511,10 @@ substringUnsafe : Str, Nat, Nat -> Str
## Returns the given [Str] with each occurrence of a substring replaced.
## Returns [Err NotFound] if the substring is not found.
##
## expect Str.replaceEach "foo/bar/baz" "/" "_" == Ok "foo_bar_baz"
## expect Str.replaceEach "not here" "/" "_" == Err NotFound
## ```
## expect Str.replaceEach "foo/bar/baz" "/" "_" == Ok "foo_bar_baz"
## expect Str.replaceEach "not here" "/" "_" == Err NotFound
## ```
replaceEach : Str, Str, Str -> Result Str [NotFound]
replaceEach = \haystack, needle, flower ->
when splitFirst haystack needle is
@ -515,9 +544,10 @@ expect Str.replaceEach "abXdeXghi" "X" "_" == Ok "ab_de_ghi"
## Returns the given [Str] with the first occurrence of a substring replaced.
## Returns [Err NotFound] if the substring is not found.
##
## expect Str.replaceFirst "foo/bar/baz" "/" "_" == Ok "foo_bar/baz"
## expect Str.replaceFirst "no slashes here" "/" "_" == Err NotFound
## ```
## expect Str.replaceFirst "foo/bar/baz" "/" "_" == Ok "foo_bar/baz"
## expect Str.replaceFirst "no slashes here" "/" "_" == Err NotFound
## ```
replaceFirst : Str, Str, Str -> Result Str [NotFound]
replaceFirst = \haystack, needle, flower ->
when splitFirst haystack needle is
@ -530,9 +560,10 @@ expect Str.replaceFirst "abXdeXghi" "X" "_" == Ok "ab_deXghi"
## Returns the given [Str] with the last occurrence of a substring replaced.
## Returns [Err NotFound] if the substring is not found.
##
## expect Str.replaceLast "foo/bar/baz" "/" "_" == Ok "foo/bar_baz"
## expect Str.replaceLast "no slashes here" "/" "_" == Err NotFound
## ```
## expect Str.replaceLast "foo/bar/baz" "/" "_" == Ok "foo/bar_baz"
## expect Str.replaceLast "no slashes here" "/" "_" == Err NotFound
## ```
replaceLast : Str, Str, Str -> Result Str [NotFound]
replaceLast = \haystack, needle, flower ->
when splitLast haystack needle is
@ -546,9 +577,10 @@ expect Str.replaceLast "abXdeXghi" "X" "_" == Ok "abXde_ghi"
## Returns the given [Str] before the first occurrence of a [delimiter](https://www.computerhope.com/jargon/d/delimite.htm), as well
## as the rest of the string after that occurrence.
## Returns [ Err NotFound] if the delimiter is not found.
##
## expect Str.splitFirst "foo/bar/baz" "/" == Ok { before: "foo", after: "bar/baz" }
## expect Str.splitFirst "no slashes here" "/" == Err NotFound
## ```
## expect Str.splitFirst "foo/bar/baz" "/" == Ok { before: "foo", after: "bar/baz" }
## expect Str.splitFirst "no slashes here" "/" == Err NotFound
## ```
splitFirst : Str, Str -> Result { before : Str, after : Str } [NotFound]
splitFirst = \haystack, needle ->
when firstMatch haystack needle is
@ -599,9 +631,10 @@ firstMatchHelp = \haystack, needle, index, lastPossible ->
## Returns the given [Str] before the last occurrence of a delimiter, as well as
## the rest of the string after that occurrence.
## Returns [Err NotFound] if the delimiter is not found.
##
## expect Str.splitLast "foo/bar/baz" "/" == Ok { before: "foo/bar", after: "baz" }
## expect Str.splitLast "no slashes here" "/" == Err NotFound
## ```
## expect Str.splitLast "foo/bar/baz" "/" == Ok { before: "foo/bar", after: "baz" }
## expect Str.splitLast "no slashes here" "/" == Err NotFound
## ```
splitLast : Str, Str -> Result { before : Str, after : Str } [NotFound]
splitLast = \haystack, needle ->
when lastMatch haystack needle is
@ -690,10 +723,11 @@ matchesAtHelp = \state ->
## Walks over the `UTF-8` bytes of the given [Str] and calls a function to update
## state for each byte. The index for that byte in the string is provided
## to the update function.
##
## f : List U8, U8, Nat -> List U8
## f = \state, byte, _ -> List.append state byte
## expect Str.walkUtf8WithIndex "ABC" [] f == [65, 66, 67]
## ```
## f : List U8, U8, Nat -> List U8
## f = \state, byte, _ -> List.append state byte
## expect Str.walkUtf8WithIndex "ABC" [] f == [65, 66, 67]
## ```
walkUtf8WithIndex : Str, state, (state, U8, Nat -> state) -> state
walkUtf8WithIndex = \string, state, step ->
walkUtf8WithIndexHelp string state step 0 (Str.countUtf8Bytes string)
@ -716,9 +750,10 @@ appendScalarUnsafe : Str, U32 -> Str
## Append a [U32] scalar to the given string. If the given scalar is not a valid
## unicode value, it returns [Err InvalidScalar].
##
## expect Str.appendScalar "H" 105 == Ok "Hi"
## expect Str.appendScalar "😢" 0xabcdef == Err InvalidScalar
## ```
## expect Str.appendScalar "H" 105 == Ok "Hi"
## expect Str.appendScalar "😢" 0xabcdef == Err InvalidScalar
## ```
appendScalar : Str, U32 -> Result Str [InvalidScalar]
appendScalar = \string, scalar ->
if isValidScalar scalar then
@ -734,10 +769,11 @@ getScalarUnsafe : Str, Nat -> { scalar : U32, bytesParsed : Nat }
## Walks over the unicode [U32] values for the given [Str] and calls a function
## to update state for each.
##
## f : List U32, U32 -> List U32
## f = \state, scalar -> List.append state scalar
## expect Str.walkScalars "ABC" [] f == [65, 66, 67]
## ```
## f : List U32, U32 -> List U32
## f = \state, scalar -> List.append state scalar
## expect Str.walkScalars "ABC" [] f == [65, 66, 67]
## ```
walkScalars : Str, state, (state, U32 -> state) -> state
walkScalars = \string, init, step ->
walkScalarsHelp string init step 0 (Str.countUtf8Bytes string)
@ -754,16 +790,17 @@ walkScalarsHelp = \string, state, step, index, length ->
## Walks over the unicode [U32] values for the given [Str] and calls a function
## to update state for each.
##
## f : List U32, U32 -> [Break (List U32), Continue (List U32)]
## f = \state, scalar ->
## check = 66
## if scalar == check then
## Break [check]
## else
## Continue (List.append state scalar)
## expect Str.walkScalarsUntil "ABC" [] f == [66]
## expect Str.walkScalarsUntil "AxC" [] f == [65, 120, 67]
## ```
## f : List U32, U32 -> [Break (List U32), Continue (List U32)]
## f = \state, scalar ->
## check = 66
## if scalar == check then
## Break [check]
## else
## Continue (List.append state scalar)
## expect Str.walkScalarsUntil "ABC" [] f == [66]
## expect Str.walkScalarsUntil "AxC" [] f == [65, 120, 67]
## ```
walkScalarsUntil : Str, state, (state, U32 -> [Break state, Continue state]) -> state
walkScalarsUntil = \string, init, step ->
walkScalarsUntilHelp string init step 0 (Str.countUtf8Bytes string)
@ -795,7 +832,8 @@ strToNumHelp = \string ->
Err InvalidNumStr
## Adds a prefix to the given [Str].
##
## expect Str.withPrefix "Awesome" "Roc" == "RocAwesome"
## ```
## expect Str.withPrefix "Awesome" "Roc" == "RocAwesome"
## ```
withPrefix : Str, Str -> Str
withPrefix = \str, prefix -> Str.concat prefix str