# LLM Docs ## Compact Docs Copy just this section if you want to keep your context small. If you want all docs, check the section `Full Docs per Module` below. Str.Utf8Problem : [ InvalidStartByte, UnexpectedEndOfSequence, ExpectedContinuation, OverlongEncoding, CodepointTooLarge, EncodesSurrogateHalf ] Str.is_empty : Str -> Bool Str.concat : Str, Str -> Str Str.with_capacity : U64 -> Str Str.reserve : Str, U64 -> Str Str.join_with : List Str, Str -> Str Str.split_on : Str, Str -> List Str Str.repeat : Str, U64 -> Str Str.len : Str -> [LearnAboutStringsInRoc Str] Str.to_utf8 : Str -> List U8 Str.from_utf8 : List U8 -> Result Str [ BadUtf8 { problem : Utf8Problem, index : U64 } ] Str.from_utf8_lossy : List U8 -> Str Str.from_utf16 : List U16 -> Result Str [ BadUtf16 { problem : Utf8Problem, index : U64 } ] Str.from_utf16_lossy : List U16 -> Str Str.from_utf32 : List U32 -> Result Str [ BadUtf32 { problem : Utf8Problem, index : U64 } ] Str.from_utf32_lossy : List U32 -> Str Str.starts_with : Str, Str -> Bool Str.ends_with : Str, Str -> Bool Str.trim : Str -> Str Str.trim_start : Str -> Str Str.trim_end : Str -> Str Str.to_dec : Str -> Result Dec [InvalidNumStr] Str.to_f64 : Str -> Result F64 [InvalidNumStr] Str.to_f32 : Str -> Result F32 [InvalidNumStr] Str.to_u128 : Str -> Result U128 [InvalidNumStr] Str.to_i128 : Str -> Result I128 [InvalidNumStr] Str.to_u64 : Str -> Result U64 [InvalidNumStr] Str.to_i64 : Str -> Result I64 [InvalidNumStr] Str.to_u32 : Str -> Result U32 [InvalidNumStr] Str.to_i32 : Str -> Result I32 [InvalidNumStr] Str.to_u16 : Str -> Result U16 [InvalidNumStr] Str.to_i16 : Str -> Result I16 [InvalidNumStr] Str.to_u8 : Str -> Result U8 [InvalidNumStr] Str.to_i8 : Str -> Result I8 [InvalidNumStr] Str.count_utf8_bytes : Str -> U64 Str.replace_each : Str, Str, Str -> Str Str.replace_first : Str, Str, Str -> Str Str.replace_last : Str, Str, Str -> Str Str.split_first : Str, Str -> Result { before : Str, after : Str } [NotFound] Str.split_last : Str, Str -> Result { before : Str, after : Str } [NotFound] Str.walk_utf8_with_index : Str, state, (state, U8, U64 -> state) -> state Str.walk_utf8 : Str, state, (state, U8 -> state) -> state Str.release_excess_capacity : Str -> Str Str.with_prefix : Str, Str -> Str Str.contains : Str, Str -> Bool Str.drop_prefix : Str, Str -> Str Str.drop_suffix : Str, Str -> Str Str.with_ascii_lowercased : Str -> Str Str.with_ascii_uppercased : Str -> Str Str.caseless_ascii_equals : Str, Str -> Bool Num.e : Frac * Num.pi : Frac * Num.tau : Frac * Num.to_str : Num * -> Str Num.int_cast : Int a -> Int b Num.compare : Num a, Num a -> [ LT, EQ, GT ] Num.is_lt : Num a, Num a -> Bool Num.is_gt : Num a, Num a -> Bool Num.is_lte : Num a, Num a -> Bool Num.is_gte : Num a, Num a -> Bool Num.is_approx_eq : Frac a, Frac a, { rtol ? Frac a, atol ? Frac a } -> Bool Num.is_zero : Num a -> Bool Num.is_even : Int a -> Bool Num.is_odd : Int a -> Bool Num.is_positive : Num a -> Bool Num.is_negative : Num a -> Bool Num.to_frac : Num * -> Frac * Num.is_nan : Frac * -> Bool Num.is_infinite : Frac * -> Bool Num.is_finite : Frac * -> Bool Num.abs : Num a -> Num a Num.abs_diff : Num a, Num a -> Num a Num.neg : Num a -> Num a Num.add : Num a, Num a -> Num a Num.sub : Num a, Num a -> Num a Num.mul : Num a, Num a -> Num a Num.min : Num a, Num a -> Num a Num.max : Num a, Num a -> Num a Num.sin : Frac a -> Frac a Num.cos : Frac a -> Frac a Num.tan : Frac a -> Frac a Num.asin : Frac a -> Frac a Num.acos : Frac a -> Frac a Num.atan : Frac a -> Frac a Num.sqrt : Frac a -> Frac a Num.sqrt_checked : Frac a -> Result (Frac a) [SqrtOfNegative] Num.log : Frac a -> Frac a Num.log_checked : Frac a -> Result (Frac a) [LogNeedsPositive] Num.div : Frac a, Frac a -> Frac a Num.div_checked : Frac a, Frac a -> Result (Frac a) [DivByZero] Num.div_ceil : Int a, Int a -> Int a Num.div_ceil_checked : Int a, Int a -> Result (Int a) [DivByZero] Num.div_trunc : Int a, Int a -> Int a Num.div_trunc_checked : Int a, Int a -> Result (Int a) [DivByZero] Num.rem : Int a, Int a -> Int a Num.rem_checked : Int a, Int a -> Result (Int a) [DivByZero] Num.is_multiple_of : Int a, Int a -> Bool Num.bitwise_and : Int a, Int a -> Int a Num.bitwise_xor : Int a, Int a -> Int a Num.bitwise_or : Int a, Int a -> Int a Num.bitwise_not : Int a -> Int a Num.shift_left_by : Int a, U8 -> Int a Num.shift_right_by : Int a, U8 -> Int a Num.shift_right_zf_by : Int a, U8 -> Int a Num.round : Frac * -> Int * Num.floor : Frac * -> Int * Num.ceiling : Frac * -> Int * Num.pow : Frac a, Frac a -> Frac a Num.pow_int : Int a, Int a -> Int a Num.count_leading_zero_bits : Int a -> U8 Num.count_trailing_zero_bits : Int a -> U8 Num.count_one_bits : Int a -> U8 Num.add_wrap : Int range, Int range -> Int range Num.add_saturated : Num a, Num a -> Num a Num.add_checked : Num a, Num a -> Result (Num a) [Overflow] Num.sub_wrap : Int range, Int range -> Int range Num.sub_saturated : Num a, Num a -> Num a Num.sub_checked : Num a, Num a -> Result (Num a) [Overflow] Num.mul_wrap : Int range, Int range -> Int range Num.mul_saturated : Num a, Num a -> Num a Num.mul_checked : Num a, Num a -> Result (Num a) [Overflow] Num.min_i8 : I8 Num.max_i8 : I8 Num.min_u8 : U8 Num.max_u8 : U8 Num.min_i16 : I16 Num.max_i16 : I16 Num.min_u16 : U16 Num.max_u16 : U16 Num.min_i32 : I32 Num.max_i32 : I32 Num.min_u32 : U32 Num.max_u32 : U32 Num.min_i64 : I64 Num.max_i64 : I64 Num.min_u64 : U64 Num.max_u64 : U64 Num.min_i128 : I128 Num.max_i128 : I128 Num.min_u128 : U128 Num.max_u128 : U128 Num.min_f32 : F32 Num.max_f32 : F32 Num.min_f64 : F64 Num.max_f64 : F64 Num.to_i8 : Int * -> I8 Num.to_i16 : Int * -> I16 Num.to_i32 : Int * -> I32 Num.to_i64 : Int * -> I64 Num.to_i128 : Int * -> I128 Num.to_u8 : Int * -> U8 Num.to_u16 : Int * -> U16 Num.to_u32 : Int * -> U32 Num.to_u64 : Int * -> U64 Num.to_u128 : Int * -> U128 Num.to_f32 : Num * -> F32 Num.to_f64 : Num * -> F64 Num.to_i8_checked : Int * -> Result I8 [OutOfBounds] Num.to_i16_checked : Int * -> Result I16 [OutOfBounds] Num.to_i32_checked : Int * -> Result I32 [OutOfBounds] Num.to_i64_checked : Int * -> Result I64 [OutOfBounds] Num.to_i128_checked : Int * -> Result I128 [OutOfBounds] Num.to_u8_checked : Int * -> Result U8 [OutOfBounds] Num.to_u16_checked : Int * -> Result U16 [OutOfBounds] Num.to_u32_checked : Int * -> Result U32 [OutOfBounds] Num.to_u64_checked : Int * -> Result U64 [OutOfBounds] Num.to_u128_checked : Int * -> Result U128 [OutOfBounds] Num.to_f32_checked : Num * -> Result F32 [OutOfBounds] Num.to_f64_checked : Num * -> Result F64 [OutOfBounds] Num.without_decimal_point : Dec -> I128 Num.with_decimal_point : I128 -> Dec Num.f32_to_parts : F32 -> { sign : Bool, exponent : U8, fraction : U32 } Num.f64_to_parts : F64 -> { sign : Bool, exponent : U16, fraction : U64 } Num.f32_from_parts : { sign : Bool, exponent : U8, fraction : U32 } -> F32 Num.f64_from_parts : { sign : Bool, exponent : U16, fraction : U64 } -> F64 Num.f32_to_bits : F32 -> U32 Num.f64_to_bits : F64 -> U64 Num.dec_to_bits : Dec -> U128 Num.f32_from_bits : U32 -> F32 Num.f64_from_bits : U64 -> F64 Num.dec_from_bits : U128 -> Dec Num.from_bool : Bool -> Num * Num.nan_f32 : F32 Num.nan_f64 : F64 Num.infinity_f32 : F32 Num.infinity_f64 : F64 Bool.Eq : implements is_eq : a, a -> Bool where a implements Eq Bool.true : Bool Bool.false : Bool Bool.not : Bool -> Bool Bool.is_not_eq : a, a -> Bool where a implements Eq Result.Result : [ Ok ok, Err err ] Result.is_ok : Result ok err -> Bool Result.is_err : Result ok err -> Bool Result.with_default : Result ok err, ok -> ok Result.map_ok : Result a err, (a -> b) -> Result b err Result.map_err : Result ok a, (a -> b) -> Result ok b Result.on_err : Result a err, (err -> Result a other_err) -> Result a other_err Result.on_err! : Result a err, (err => Result a other_err) => Result a other_err Result.map_both : Result ok1 err1, (ok1 -> ok2), (err1 -> err2) -> Result ok2 err2 Result.map2 : Result a err, Result b err, (a, b -> c) -> Result c err Result.try : Result a err, (a -> Result b err) -> Result b err List.is_empty : List * -> Bool List.get : List a, U64 -> Result a [OutOfBounds] List.replace : List a, U64, a -> { list : List a, value : a } List.set : List a, U64, a -> List a List.update : List a, U64, (a -> a) -> List a List.append : List a, a -> List a List.append_if_ok : List a, Result a * -> List a List.prepend : List a, a -> List a List.prepend_if_ok : List a, Result a * -> List a List.len : List * -> U64 List.with_capacity : U64 -> List * List.reserve : List a, U64 -> List a List.release_excess_capacity : List a -> List a List.concat : List a, List a -> List a List.last : List a -> Result a [ListWasEmpty] List.single : a -> List a List.repeat : a, U64 -> List a List.reverse : List a -> List a List.join : List (List a) -> List a List.contains : List a, a -> Bool where a implements Eq List.walk : List elem, state, (state, elem -> state) -> state List.walk_with_index : List elem, state, (state, elem, U64 -> state) -> state List.walk_with_index_until : List elem, state, (state, elem, U64 -> [ Continue state, Break state ]) -> state List.walk_backwards : List elem, state, (state, elem -> state) -> state List.walk_until : List elem, state, (state, elem -> [ Continue state, Break state ]) -> state List.walk_backwards_until : List elem, state, (state, elem -> [ Continue state, Break state ]) -> state List.walk_from : List elem, U64, state, (state, elem -> state) -> state List.walk_from_until : List elem, U64, state, (state, elem -> [ Continue state, Break state ]) -> state List.sum : List (Num a) -> Num a List.product : List (Num a) -> Num a List.any : List a, (a -> Bool) -> Bool List.all : List a, (a -> Bool) -> Bool List.keep_if : List a, (a -> Bool) -> List a List.keep_if_try! : List a, (a => Result Bool err) => Result (List a) err List.drop_if : List a, (a -> Bool) -> List a List.count_if : List a, (a -> Bool) -> U64 List.keep_oks : List before, (before -> Result after *) -> List after List.keep_errs : List before, (before -> Result * after) -> List after List.map : List a, (a -> b) -> List b List.map2 : List a, List b, (a, b -> c) -> List c List.map3 : List a, List b, List c, (a, b, c -> d) -> List d List.map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e List.map_with_index : List a, (a, U64 -> b) -> List b List.sort_with : List a, (a, a -> [ LT, EQ, GT ]) -> List a List.sort_asc : List (Num a) -> List (Num a) List.sort_desc : List (Num a) -> List (Num a) List.swap : List a, U64, U64 -> List a List.first : List a -> Result a [ListWasEmpty] List.take_first : List elem, U64 -> List elem List.take_last : List elem, U64 -> List elem List.drop_first : List elem, U64 -> List elem List.drop_last : List elem, U64 -> List elem List.drop_at : List elem, U64 -> List elem List.min : List (Num a) -> Result (Num a) [ListWasEmpty] List.max : List (Num a) -> Result (Num a) [ListWasEmpty] List.join_map : List a, (a -> List b) -> List b List.find_first : List elem, (elem -> Bool) -> Result elem [NotFound] List.find_last : List elem, (elem -> Bool) -> Result elem [NotFound] List.find_first_index : List elem, (elem -> Bool) -> Result U64 [NotFound] List.find_last_index : List elem, (elem -> Bool) -> Result U64 [NotFound] List.sublist : List elem, { start : U64, len : U64 } -> List elem List.intersperse : List elem, elem -> List elem List.starts_with : List elem, List elem -> Bool where elem implements Eq List.ends_with : List elem, List elem -> Bool where elem implements Eq List.split_at : List elem, U64 -> { before : List elem, others : List elem } List.split_on : List a, a -> List (List a) where a implements Eq List.split_on_list : List a, List a -> List (List a) where a implements Eq List.split_first : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] where elem implements Eq List.split_last : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] where elem implements Eq List.chunks_of : List a, U64 -> List (List a) List.map_try : List elem, (elem -> Result ok err) -> Result (List ok) err List.map_try! : List elem, (elem => Result ok err) => Result (List ok) err List.walk_try : List elem, state, (state, elem -> Result state err) -> Result state err List.concat_utf8 : List U8, Str -> List U8 List.for_each! : List a, (a => {}) => {} List.for_each_try! : List a, (a => Result {} err) => Result {} err List.walk! : List elem, state, (state, elem => state) => state List.walk_try! : List elem, state, (state, elem => Result state err) => Result state err Dict.empty : {} -> Dict * * Dict.with_capacity : U64 -> Dict * * Dict.reserve : Dict k v, U64 -> Dict k v Dict.release_excess_capacity : Dict k v -> Dict k v Dict.capacity : Dict * * -> U64 Dict.single : k, v -> Dict k v Dict.from_list : List ( k, v ) -> Dict k v Dict.len : Dict * * -> U64 Dict.is_empty : Dict * * -> Bool Dict.clear : Dict k v -> Dict k v Dict.map : Dict k a, (k, a -> b) -> Dict k b Dict.join_map : Dict a b, (a, b -> Dict x y) -> Dict x y Dict.walk : Dict k v, state, (state, k, v -> state) -> state Dict.walk_until : Dict k v, state, (state, k, v -> [ Continue state, Break state ]) -> state Dict.keep_if : Dict k v, ( ( k, v ) -> Bool) -> Dict k v Dict.drop_if : Dict k v, ( ( k, v ) -> Bool) -> Dict k v Dict.get : Dict k v, k -> Result v [KeyNotFound] Dict.contains : Dict k v, k -> Bool Dict.insert : Dict k v, k, v -> Dict k v Dict.remove : Dict k v, k -> Dict k v Dict.update : Dict k v, k, (Result v [Missing] -> Result v [Missing]) -> Dict k v Dict.to_list : Dict k v -> List ( k, v ) Dict.keys : Dict k v -> List k Dict.values : Dict k v -> List v Dict.insert_all : Dict k v, Dict k v -> Dict k v Dict.keep_shared : Dict k v, Dict k v -> Dict k v where v implements Eq Dict.remove_all : Dict k v, Dict k v -> Dict k v Set.empty : {} -> Set * Set.with_capacity : U64 -> Set * Set.reserve : Set k, U64 -> Set k Set.release_excess_capacity : Set k -> Set k Set.single : k -> Set k Set.insert : Set k, k -> Set k Set.len : Set * -> U64 Set.capacity : Set * -> U64 Set.is_empty : Set * -> Bool Set.remove : Set k, k -> Set k Set.contains : Set k, k -> Bool Set.to_list : Set k -> List k Set.from_list : List k -> Set k Set.union : Set k, Set k -> Set k Set.intersection : Set k, Set k -> Set k Set.difference : Set k, Set k -> Set k Set.walk : Set k, state, (state, k -> state) -> state Set.map : Set a, (a -> b) -> Set b Set.join_map : Set a, (a -> Set b) -> Set b Set.walk_until : Set k, state, (state, k -> [ Continue state, Break state ]) -> state Set.keep_if : Set k, (k -> Bool) -> Set k Set.drop_if : Set k, (k -> Bool) -> Set k Decode.DecodeError : [TooShort] Decode.Decoding : implements decoder : Decoder val fmt where val implements Decoding, fmt implements DecoderFormatting Decode.DecoderFormatting : implements u8 : Decoder U8 fmt where fmt implements DecoderFormatting u16 : Decoder U16 fmt where fmt implements DecoderFormatting u32 : Decoder U32 fmt where fmt implements DecoderFormatting u64 : Decoder U64 fmt where fmt implements DecoderFormatting u128 : Decoder U128 fmt where fmt implements DecoderFormatting i8 : Decoder I8 fmt where fmt implements DecoderFormatting i16 : Decoder I16 fmt where fmt implements DecoderFormatting i32 : Decoder I32 fmt where fmt implements DecoderFormatting i64 : Decoder I64 fmt where fmt implements DecoderFormatting i128 : Decoder I128 fmt where fmt implements DecoderFormatting f32 : Decoder F32 fmt where fmt implements DecoderFormatting f64 : Decoder F64 fmt where fmt implements DecoderFormatting dec : Decoder Dec fmt where fmt implements DecoderFormatting bool : Decoder Bool fmt where fmt implements DecoderFormatting string : Decoder Str fmt where fmt implements DecoderFormatting list : Decoder elem fmt -> Decoder (List elem) fmt where fmt implements DecoderFormatting record : state, (state, Str -> [ Keep (Decoder state fmt), Skip ]), (state, fmt -> Result val DecodeError) -> Decoder val fmt where fmt implements DecoderFormatting tuple : state, (state, U64 -> [ Next (Decoder state fmt), TooLong ]), (state -> Result val DecodeError) -> Decoder val fmt where fmt implements DecoderFormatting Decode.custom : (List U8, fmt -> DecodeResult val) -> Decoder val fmt where fmt implements DecoderFormatting Decode.decode_with : List U8, Decoder val fmt, fmt -> DecodeResult val where fmt implements DecoderFormatting Decode.from_bytes_partial : List U8, fmt -> DecodeResult val where val implements Decoding, fmt implements DecoderFormatting Decode.from_bytes : List U8, fmt -> Result val [Leftover (List U8)]DecodeError where val implements Decoding, fmt implements DecoderFormatting Decode.map_result : DecodeResult a, (a -> b) -> DecodeResult b Encode.Encoding : implements to_encoder : val -> Encoder fmt where val implements Encoding, fmt implements EncoderFormatting Encode.EncoderFormatting : implements u8 : U8 -> Encoder fmt where fmt implements EncoderFormatting u16 : U16 -> Encoder fmt where fmt implements EncoderFormatting u32 : U32 -> Encoder fmt where fmt implements EncoderFormatting u64 : U64 -> Encoder fmt where fmt implements EncoderFormatting u128 : U128 -> Encoder fmt where fmt implements EncoderFormatting i8 : I8 -> Encoder fmt where fmt implements EncoderFormatting i16 : I16 -> Encoder fmt where fmt implements EncoderFormatting i32 : I32 -> Encoder fmt where fmt implements EncoderFormatting i64 : I64 -> Encoder fmt where fmt implements EncoderFormatting i128 : I128 -> Encoder fmt where fmt implements EncoderFormatting f32 : F32 -> Encoder fmt where fmt implements EncoderFormatting f64 : F64 -> Encoder fmt where fmt implements EncoderFormatting dec : Dec -> Encoder fmt where fmt implements EncoderFormatting bool : Bool -> Encoder fmt where fmt implements EncoderFormatting string : Str -> Encoder fmt where fmt implements EncoderFormatting list : List elem, (elem -> Encoder fmt) -> Encoder fmt where fmt implements EncoderFormatting record : List { key : Str, value : Encoder fmt } -> Encoder fmt where fmt implements EncoderFormatting tuple : List (Encoder fmt) -> Encoder fmt where fmt implements EncoderFormatting tag : Str, List (Encoder fmt) -> Encoder fmt where fmt implements EncoderFormatting Encode.custom : (List U8, fmt -> List U8) -> Encoder fmt where fmt implements EncoderFormatting Encode.append_with : List U8, Encoder fmt, fmt -> List U8 where fmt implements EncoderFormatting Encode.append : List U8, val, fmt -> List U8 where val implements Encoding, fmt implements EncoderFormatting Encode.to_bytes : val, fmt -> List U8 where val implements Encoding, fmt implements EncoderFormatting Hash.Hash : implements hash : hasher, a -> hasher where a implements Hash, hasher implements Hasher Hash.Hasher : implements add_bytes : a, List U8 -> a where a implements Hasher add_u8 : a, U8 -> a where a implements Hasher add_u16 : a, U16 -> a where a implements Hasher add_u32 : a, U32 -> a where a implements Hasher add_u64 : a, U64 -> a where a implements Hasher add_u128 : a, U128 -> a where a implements Hasher complete : a -> U64 where a implements Hasher Hash.hash_bool : a, Bool -> a where a implements Hasher Hash.hash_i8 : a, I8 -> a where a implements Hasher Hash.hash_i16 : a, I16 -> a where a implements Hasher Hash.hash_i32 : a, I32 -> a where a implements Hasher Hash.hash_i64 : a, I64 -> a where a implements Hasher Hash.hash_i128 : a, I128 -> a where a implements Hasher Hash.hash_dec : a, Dec -> a where a implements Hasher Box.box : a -> Box a Box.unbox : Box a -> a Inspect.KeyValWalker : collection, state, (state, key, val -> state) -> state Inspect.ElemWalker : collection, state, (state, elem -> state) -> state Inspect.InspectFormatter : implements init : {} -> f where f implements InspectFormatter tag : Str, List (Inspector f) -> Inspector f where f implements InspectFormatter tuple : List (Inspector f) -> Inspector f where f implements InspectFormatter record : List { key : Str, value : Inspector f } -> Inspector f where f implements InspectFormatter bool : Bool -> Inspector f where f implements InspectFormatter str : Str -> Inspector f where f implements InspectFormatter list : list, ElemWalker state list elem, (elem -> Inspector f) -> Inspector f where f implements InspectFormatter set : set, ElemWalker state set elem, (elem -> Inspector f) -> Inspector f where f implements InspectFormatter dict : dict, KeyValWalker state dict key value, (key -> Inspector f), (value -> Inspector f) -> Inspector f where f implements InspectFormatter opaque : * -> Inspector f where f implements InspectFormatter function : * -> Inspector f where f implements InspectFormatter u8 : U8 -> Inspector f where f implements InspectFormatter i8 : I8 -> Inspector f where f implements InspectFormatter u16 : U16 -> Inspector f where f implements InspectFormatter i16 : I16 -> Inspector f where f implements InspectFormatter u32 : U32 -> Inspector f where f implements InspectFormatter i32 : I32 -> Inspector f where f implements InspectFormatter u64 : U64 -> Inspector f where f implements InspectFormatter i64 : I64 -> Inspector f where f implements InspectFormatter u128 : U128 -> Inspector f where f implements InspectFormatter i128 : I128 -> Inspector f where f implements InspectFormatter f32 : F32 -> Inspector f where f implements InspectFormatter f64 : F64 -> Inspector f where f implements InspectFormatter dec : Dec -> Inspector f where f implements InspectFormatter Inspect.custom : (f -> f) -> Inspector f where f implements InspectFormatter Inspect.apply : Inspector f, f -> f where f implements InspectFormatter Inspect.Inspect : implements to_inspector : val -> Inspector f where val implements Inspect, f implements InspectFormatter Inspect.inspect : val -> f where val implements Inspect, f implements InspectFormatter Inspect.to_str : val -> Str where val implements Inspect ## Full Docs per Module ### Str Utf8Problem : [ InvalidStartByte, UnexpectedEndOfSequence, ExpectedContinuation, OverlongEncoding, CodepointTooLarge, EncodesSurrogateHalf ] is_empty : Str -> Bool Description: Returns [Bool.true] if the string is empty, and [Bool.false] otherwise. ```roc expect Str.is_empty("hi!") == Bool.false expect Str.is_empty("") == Bool.true ``` concat : Str, Str -> Str Description: Concatenates two strings together. ```roc expect Str.concat("ab", "cd") == "abcd" expect Str.concat("hello", "") == "hello" expect Str.concat("", "") == "" ``` with_capacity : U64 -> Str Description: Returns a string of the specified capacity without any content. This is a performance optimization tool that's like calling [Str.reserve] on an empty string. It's useful when you plan to build up a string incrementally, for example by calling [Str.concat] on it: ```roc greeting = "Hello and welcome to Roc" subject = "Awesome Programmer" # Evaluates to "Hello and welcome to Roc, Awesome Programmer!" hello_world = Str.with_capacity(45) |> Str.concat(greeting) |> Str.concat(", ") |> Str.concat(subject) |> Str.concat("!") ``` In general, if you plan to use [Str.concat] on an empty string, it will be faster to start with [Str.with_capacity] than with `""`. Even if you don't know the exact capacity of the string, giving [with_capacity] a higher value than ends up being necessary can help prevent reallocation and copying—at the cost of using more memory than is necessary. For more details on how the performance optimization works, see [Str.reserve]. reserve : Str, U64 -> Str Description: Increase a string's capacity by at least the given number of additional bytes. This can improve the performance of string concatenation operations like [Str.concat] by allocating extra capacity up front, which can prevent the need for reallocations and copies. Consider the following example which does not use [Str.reserve]: ```roc greeting = "Hello and welcome to Roc" subject = "Awesome Programmer" # Evaluates to "Hello and welcome to Roc, Awesome Programmer!" hello_world = greeting |> Str.concat(", ") |> Str.concat(subject) |> Str.concat("!") ``` In this example: 1. We start with `greeting`, which has both a length and capacity of 24 (bytes). 2. `|> Str.concat ", "` will see that there isn't enough capacity to add 2 more bytes for the `", "`, so it will create a new heap allocation with enough bytes to hold both. (This probably will be more than 7 bytes, because when [Str] functions reallocate, they apply a multiplier to the exact capacity required. This makes it less likely that future realloctions will be needed. The multiplier amount is not specified, because it may change in future releases of Roc, but it will likely be around 1.5 to 2 times the exact capacity required.) Then it will copy the current bytes (`"Hello"`) into the new allocation, and finally concatenate the `", "` into the new allocation. The old allocation will then be deallocated because it's no longer referenced anywhere in the program. 3. `|> Str.concat subject` will again check if there is enough capacity in the string. If it doesn't find enough capacity once again, it will make a third allocation, copy the existing bytes (`"Hello, "`) into that third allocation, and then deallocate the second allocation because it's already no longer being referenced anywhere else in the program. (It may find enough capacity in this particular case, because the previous [Str.concat] allocated something like 1.5 to 2 times the necessary capacity in order to anticipate future concatenations like this...but if something longer than `"World"` were being concatenated here, it might still require further reallocation and copying.) 4. `|> Str.concat "!\n"` will repeat this process once more. This process can have significant performance costs due to multiple reallocation of new strings, copying between old strings and new strings, and deallocation of immediately obsolete strings. Here's a modified example which uses [Str.reserve] to eliminate the need for all that reallocation, copying, and deallocation. ```roc hello_world = greeting |> Str.reserve(21) |> Str.concat(", ") |> Str.concat(subject) |> Str.concat("!") ``` In this example: 1. We again start with `greeting`, which has both a length and capacity of 24 bytes. 2. `|> Str.reserve(21)` will ensure that there is enough capacity in the string for an additional 21 bytes (to make room for `", "`, `"Awesome Programmer"`, and `"!"`). Since the current capacity is only 24, it will create a new 45-byte (24 + 21) heap allocation and copy the contents of the existing allocation (`greeting`) into it. 3. `|> Str.concat(", ")` will concatenate `, ` to the string. No reallocation, copying, or deallocation will be necessary, because the string already has a capacity of 45 btytes, and `greeting` will only use 24 of them. 4. `|> Str.concat(subject)` will concatenate `subject` (`"Awesome Programmer"`) to the string. Again, no reallocation, copying, or deallocation will be necessary. 5. `|> Str.concat "!\n"` will concatenate `"!\n"` to the string, still without any reallocation, copying, or deallocation. Here, [Str.reserve] prevented multiple reallocations, copies, and deallocations during the [Str.concat] calls. Notice that it did perform a heap allocation before any [Str.concat] calls were made, which means that using [Str.reserve] is not free! You should only use it if you actually expect to make use of the extra capacity. Ideally, you'd be able to predict exactly how many extra bytes of capacity will be needed, but this may not always be knowable. When you don't know exactly how many bytes to reserve, you can often get better performance by choosing a number of bytes that's too high, because a number that's too low could lead to reallocations. There's a limit to this, of course; if you always give it ten times what it turns out to need, that could prevent reallocations but will also waste a lot of memory! If you plan to use [Str.reserve] on an empty string, it's generally better to use [Str.with_capacity] instead. join_with : List Str, Str -> Str Description: Combines a [List] of strings into a single string, with a separator string in between each. ```roc expect Str.join_with(["one", "two", "three"], ", ") == "one, two, three" expect Str.join_with(["1", "2", "3", "4"], ".") == "1.2.3.4" ``` split_on : Str, Str -> List Str Description: Split a string around a separator. Passing `""` for the separator is not useful; it returns the original string wrapped in a [List]. ```roc expect Str.split_on("1,2,3", ",") == ["1","2","3"] expect Str.split_on("1,2,3", "") == ["1,2,3"] ``` repeat : Str, U64 -> Str Description: Repeats a string the given number of times. ```roc expect Str.repeat("z", 3) == "zzz" expect Str.repeat("na", 8) == "nananananananana" ``` Returns `""` when given `""` for the string or `0` for the count. ```roc expect Str.repeat("", 10) == "" expect Str.repeat("anything", 0) == "" ``` len : Str -> [LearnAboutStringsInRoc Str] Description: A stub function to help people discover [how they should handle this in Roc](https://www.roc-lang.org/faq.html#strings-in-roc). to_utf8 : Str -> List U8 Description: 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_on].) ```roc expect Str.to_utf8("Roc") == [82, 111, 99] expect Str.to_utf8("鹏") == [233, 185, 143] expect Str.to_utf8("சி") == [224, 174, 154, 224, 174, 191] expect Str.to_utf8("🐦") == [240, 159, 144, 166] ``` from_utf8 : List U8 -> Result Str [ BadUtf8 { problem : Utf8Problem, index : U64 } ] Description: 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 `[]`. ```roc expect Str.from_utf8([82, 111, 99]) == Ok("Roc") expect Str.from_utf8([233, 185, 143]) == Ok("鹏") expect Str.from_utf8([224, 174, 154, 224, 174, 191]) == Ok("சி") expect Str.from_utf8([240, 159, 144, 166]) == Ok("🐦") expect Str.from_utf8([]) == Ok("") expect Str.from_utf8([255]) |> Result.is_err ``` from_utf8_lossy : List U8 -> Str Description: Converts a [List] of [U8] UTF-8 [code units](https://unicode.org/glossary/#code_unit) to a string. Any grouping of invalid byte sequences are replaced with a single unicode replacement character '�'. An invalid byte sequence is defined as - a 2-byte-sequence starting byte, followed by less than 1 continuation byte - a 3-byte-sequence starting byte, followed by less than 2 continuation bytes - a 4-byte-sequence starting byte, followed by less than 3 continuation bytes - an invalid codepoint from the surrogate pair block - an invalid codepoint greater than 0x110000 encoded as a 4-byte sequence - any valid codepoint encoded as an incorrect sequence, for instance a codepoint that should be a 2-byte sequence encoded as a 3- or 4-byte sequence ```roc expect (Str.from_utf8_lossy [82, 111, 99, 240, 159, 144, 166]) == "Roc🐦" expect (Str.from_utf8_lossy [82, 255, 99]) == "R�c" expect (Str.from_utf8_lossy [82, 0xED, 0xA0, 0xBD, 99]) == "R�c" ``` from_utf16 : List U16 -> Result Str [ BadUtf16 { problem : Utf8Problem, index : U64 } ] Description: Converts a [List] of [U16] UTF-16 (little-endian) [code units](https://unicode.org/glossary/#code_unit) to a string. ```roc expect Str.from_utf16([82, 111, 99]) == Ok("Roc") expect Str.from_utf16([0xb9a, 0xbbf]) == Ok("சி") expect Str.from_utf16([0xd83d, 0xdc26]) == Ok("🐦") expect Str.from_utf16([]) == Ok("") # unpaired surrogates, first and second halves expect Str.from_utf16([82, 0xd83d, 99]) |> Result.is_err expect Str.from_utf16([82, 0xdc96, 99]) |> Result.is_err ``` from_utf16_lossy : List U16 -> Str Description: Converts a [List] of [U16] UTF-16 (little-endian) [code units](https://unicode.org/glossary/#code_unit) to a string. Any unpaired surrogate code unit is replaced with a single unicode replacement character '�'. ```roc expect Str.from_utf16_lossy([82, 111, 99, 0xd83d, 0xdc26]) == "Roc🐦" expect Str.from_utf16_lossy([82, 0xdc96, 99]) == "R�c" ``` from_utf32 : List U32 -> Result Str [ BadUtf32 { problem : Utf8Problem, index : U64 } ] from_utf32_lossy : List U32 -> Str Description: Converts a [List] of [U32] UTF-32 [code units](https://unicode.org/glossary/#code_unit) to a string. Any invalid code points are replaced with a single unicode replacement character '�'. ```roc expect Str.from_utf32_lossy([82, 111, 99, 0x1f426]) == "Roc🐦" expect Str.from_utf32_lossy([82, 0x110000, 99]) == "R�c" ``` starts_with : Str, Str -> Bool Description: Check if the given [Str] starts with a value. ```roc expect Str.starts_with("ABC", "A") == Bool.true expect Str.starts_with("ABC", "X") == Bool.false ``` ends_with : Str, Str -> Bool Description: Check if the given [Str] ends with a value. ```roc expect Str.ends_with("ABC", "C") == Bool.true expect Str.ends_with("ABC", "X") == Bool.false ``` trim : Str -> Str Description: Return the [Str] with all whitespace removed from both the beginning as well as the end. ```roc expect Str.trim(" Hello \n\n") == "Hello" ``` trim_start : Str -> Str Description: Return the [Str] with all whitespace removed from the beginning. ```roc expect Str.trim_start(" Hello \n\n") == "Hello \n\n" ``` trim_end : Str -> Str Description: Return the [Str] with all whitespace removed from the end. ```roc expect Str.trim_end(" Hello \n\n") == " Hello" ``` to_dec : Str -> Result Dec [InvalidNumStr] Description: 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). ```roc expect Str.to_dec("10") == Ok(10dec) expect Str.to_dec("-0.25") == Ok(-0.25dec) expect Str.to_dec("not a number") == Err(InvalidNumStr) ``` to_f64 : Str -> Result F64 [InvalidNumStr] Description: 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. ```roc expect Str.to_f64("0.10") == Ok(0.10f64) expect Str.to_f64("not a number") == Err(InvalidNumStr) ``` to_f32 : Str -> Result F32 [InvalidNumStr] Description: 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. ```roc expect Str.to_f32("0.10") == Ok(0.10f32) expect Str.to_f32("not a number") == Err(InvalidNumStr) ``` to_u128 : Str -> Result U128 [InvalidNumStr] Description: 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. ```roc expect Str.to_u128("1500") == Ok(1500u128) expect Str.to_u128("0.1") == Err(InvalidNumStr) expect Str.to_u128("-1") == Err(InvalidNumStr) expect Str.to_u128("not a number") == Err(InvalidNumStr) ``` to_i128 : Str -> Result I128 [InvalidNumStr] Description: Encode a [Str] to a signed [I128] integer. A [I128] value can hold numbers 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. ```roc expect Str.to_u128("1500") == Ok(1500i128) expect Str.to_i128("-1") == Ok(-1i128) expect Str.to_i128("0.1") == Err(InvalidNumStr) expect Str.to_i128("not a number") == Err(InvalidNumStr) ``` to_u64 : Str -> Result U64 [InvalidNumStr] Description: 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. ```roc expect Str.to_u64("1500") == Ok(1500u64) expect Str.to_u64("0.1") == Err(InvalidNumStr) expect Str.to_u64("-1") == Err(InvalidNumStr) expect Str.to_u64("not a number") == Err(InvalidNumStr) ``` to_i64 : Str -> Result I64 [InvalidNumStr] Description: 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. ```roc expect Str.to_i64("1500") == Ok(1500i64) expect Str.to_i64("-1") == Ok(-1i64) expect Str.to_i64("0.1") == Err(InvalidNumStr) expect Str.to_i64("not a number") == Err(InvalidNumStr) ``` to_u32 : Str -> Result U32 [InvalidNumStr] Description: 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. ```roc expect Str.to_u32("1500") == Ok(1500u32) expect Str.to_u32("0.1") == Err(InvalidNumStr) expect Str.to_u32("-1") == Err(InvalidNumStr) expect Str.to_u32("not a number") == Err(InvalidNumStr) ``` to_i32 : Str -> Result I32 [InvalidNumStr] Description: 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. ```roc expect Str.to_i32("1500") == Ok(1500i32) expect Str.to_i32("-1") == Ok(-1i32) expect Str.to_i32("0.1") == Err(InvalidNumStr) expect Str.to_i32("not a number") == Err(InvalidNumStr) ``` to_u16 : Str -> Result U16 [InvalidNumStr] Description: 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. ```roc expect Str.to_u16("1500") == Ok(1500u16) expect Str.to_u16("0.1") == Err(InvalidNumStr) expect Str.to_u16("-1") == Err(InvalidNumStr) expect Str.to_u16("not a number") == Err(InvalidNumStr) ``` to_i16 : Str -> Result I16 [InvalidNumStr] Description: 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. ```roc expect Str.to_i16("1500") == Ok(1500i16) expect Str.to_i16("-1") == Ok(-1i16) expect Str.to_i16("0.1") == Err(InvalidNumStr) expect Str.to_i16("not a number") == Err(InvalidNumStr) ``` to_u8 : Str -> Result U8 [InvalidNumStr] Description: 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. ```roc expect Str.to_u8("250") == Ok(250u8) expect Str.to_u8("-0.1") == Err(InvalidNumStr) expect Str.to_u8("not a number") == Err(InvalidNumStr) expect Str.to_u8("1500") == Err(InvalidNumStr) ``` to_i8 : Str -> Result I8 [InvalidNumStr] Description: 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. ```roc expect Str.to_i8("-15") == Ok(-15i8) expect Str.to_i8("150.00") == Err(InvalidNumStr) expect Str.to_i8("not a number") == Err(InvalidNumStr) ``` count_utf8_bytes : Str -> U64 Description: Gives the number of bytes in a [Str] value. ```roc expect Str.count_utf8_bytes("Hello World") == 11 ``` replace_each : Str, Str, Str -> Str Description: Returns the given [Str] with each occurrence of a substring replaced. If the substring is not found, returns the original string. ```roc expect Str.replace_each("foo/bar/baz", "/", "_") == "foo_bar_baz" expect Str.replace_each("not here", "/", "_") == "not here" ``` replace_first : Str, Str, Str -> Str Description: Returns the given [Str] with the first occurrence of a substring replaced. If the substring is not found, returns the original string. ```roc expect Str.replace_first("foo/bar/baz", "/", "_") == "foo_bar/baz" expect Str.replace_first("no slashes here", "/", "_") == "no slashes here" ``` replace_last : Str, Str, Str -> Str Description: Returns the given [Str] with the last occurrence of a substring replaced. If the substring is not found, returns the original string. ```roc expect Str.replace_last("foo/bar/baz", "/", "_") == "foo/bar_baz" expect Str.replace_last("no slashes here", "/", "_") == "no slashes here" ``` split_first : Str, Str -> Result { before : Str, after : Str } [NotFound] Description: 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. ```roc expect Str.split_first("foo/bar/baz", "/") == Ok({ before: "foo", after: "bar/baz" }) expect Str.split_first("no slashes here", "/") == Err(NotFound) ``` split_last : Str, Str -> Result { before : Str, after : Str } [NotFound] Description: 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. ```roc expect Str.split_last("foo/bar/baz", "/") == Ok({ before: "foo/bar", after: "baz" }) expect Str.split_last("no slashes here", "/") == Err(NotFound) ``` walk_utf8_with_index : Str, state, (state, U8, U64 -> state) -> state Description: 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. ```roc f : List U8, U8, U64 -> List U8 f = \state, byte, _ -> List.append(state, byte) expect Str.walk_utf8_with_index("ABC", [], f) == [65, 66, 67] ``` walk_utf8 : Str, state, (state, U8 -> state) -> state Description: Walks over the `UTF-8` bytes of the given [Str] and calls a function to update state for each byte. ```roc sum_of_utf8_bytes = Str.walk_utf8("Hello, World!", 0, (\total, byte -> total + byte )) expect sum_of_utf8_bytes == 105 ``` release_excess_capacity : Str -> Str Description: Shrink the memory footprint of a str such that its capacity and length are equal. Note: This will also convert seamless slices to regular lists. with_prefix : Str, Str -> Str Description: Adds a prefix to the given [Str]. ```roc expect Str.with_prefix("Awesome", "Roc") == "RocAwesome" ``` contains : Str, Str -> Bool Description: Determines whether or not the first Str contains the second. ```roc expect Str.contains("foobarbaz", "bar") expect !Str.contains("apple", "orange") expect Str.contains("anything", "") ``` drop_prefix : Str, Str -> Str Description: Drops the given prefix [Str] from the start of a [Str] If the prefix is not found, returns the original string. ```roc expect Str.drop_prefix("bar", "foo") == "bar" expect Str.drop_prefix("foobar", "foo") == "bar" ``` drop_suffix : Str, Str -> Str Description: Drops the given suffix [Str] from the end of a [Str] If the suffix is not found, returns the original string. ```roc expect Str.drop_suffix("bar", "foo") == "bar" expect Str.drop_suffix("barfoo", "foo") == "bar" ``` with_ascii_lowercased : Str -> Str Description: Returns a version of the string with all [ASCII characters](https://en.wikipedia.org/wiki/ASCII) lowercased. Non-ASCII characters are left unmodified. For example: ```roc expect Str.with_ascii_lowercased("CAFÉ") == "cafÉ" ``` This function is useful for things like [command-line flags](https://en.wikipedia.org/wiki/Command-line_interface#Command-line_option) and [environment variable names](https://en.wikipedia.org/wiki/Environment_variable) where you know in advance that you're dealing with a string containing only ASCII characters. It has better performance than lowercasing operations which take Unicode into account. That said, strings received from user input can always contain non-ASCII Unicode characters, and lowercasing [Unicode](https://unicode.org) works differently in different languages. For example, the string `"I"` lowercases to `"i"` in English and to `"ı"` (a [dotless i](https://en.wikipedia.org/wiki/Dotless_I)) in Turkish. These rules can also change in each [Unicode release](https://www.unicode.org/releases/), so we have separate [`unicode` package](https://github.com/roc-lang/unicode) for Unicode capitalization that can be upgraded independently from the language's builtins. To do a case-insensitive comparison of the ASCII characters in a string, you can use [Str.caseless_ascii_equals]. with_ascii_uppercased : Str -> Str Description: Returns a version of the string with all [ASCII characters](https://en.wikipedia.org/wiki/ASCII) uppercased. Non-ASCII characters are left unmodified. For example: ```roc expect Str.with_ascii_uppercased("café") == "CAFé" ``` This function is useful for things like [command-line flags](https://en.wikipedia.org/wiki/Command-line_interface#Command-line_option) and [environment variable names](https://en.wikipedia.org/wiki/Environment_variable) where you know in advance that you're dealing with a string containing only ASCII characters. It has better performance than lowercasing operations which take Unicode into account. That said, strings received from user input can always contain non-ASCII Unicode characters, and uppercasing [Unicode](https://unicode.org) works differently in different languages. For example, the string `"i"` uppercases to `"I"` in English and to `"İ"` (a [dotted I](https://en.wikipedia.org/wiki/%C4%B0)) in Turkish. These rules can also change in each Unicode release, so we have a separate [`unicode` package](https://github.com/roc-lang/unicode) for Unicode capitalization that can be upgraded independently from the language's builtins. To do a case-insensitive comparison of the ASCII characters in a string, you can use [Str.caseless_ascii_equals]. caseless_ascii_equals : Str, Str -> Bool Description: Returns `True` if all the [ASCII characters](https://en.wikipedia.org/wiki/ASCII) in the string are the same when ignoring differences in capitalization. Non-ASCII characters must all be exactly the same, including capitalization. For example: ```roc expect Str.caseless_ascii_equals("café", "CAFé") expect !Str.caseless_ascii_equals("café", "CAFÉ") ``` The first call returns `True` because all the ASCII characters are the same when ignoring differences in capitalization, and the only non-ASCII character (`é`) is the same in both strings. The second call returns `False`because `é` and `É` are not ASCII characters, and they are different. This function is useful for things like [command-line flags](https://en.wikipedia.org/wiki/Command-line_interface#Command-line_option) and [environment variable names](https://en.wikipedia.org/wiki/Environment_variable) where you know in advance that you're dealing with a string containing only ASCII characters. It has better performance than lowercasing operations which take Unicode into account. That said, strings received from user input can always contain non-ASCII Unicode characters, and lowercasing [Unicode](https://unicode.org) works differently in different languages. For example, the string `"I"` lowercases to `"i"` in English and to `"ı"` (a [dotless i](https://en.wikipedia.org/wiki/Dotless_I)) in Turkish. These rules can also change in each [Unicode release](https://www.unicode.org/releases/), so we have separate [`unicode` package](https://github.com/roc-lang/unicode) for Unicode capitalization that can be upgraded independently from the language's builtins. To convert a string's ASCII characters to uppercase or lowercase, you can use [Str.with_ascii_uppercased] or [Str.with_ascii_lowercased]. ### Num Description: 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: ```roc 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 *)`. Similarly, the number 0x1 (that is, the integer 1 in hexadecimal notation) technically has the type `Num (Integer *)`, so when you pass two of them to [Num.add], the answer you get is `2 : Num (Integer *)`. The type [`Frac a`](#Frac) is defined to be an alias for `Num (Fraction a)`, so `3.0 : Num (Fraction *)` is the same value as `3.0 : Frac *`. Similarly, the type [`Int a`](#Int) is defined to be an alias for `Num (Integer a)`, so `2 : Num (Integer *)` is the same value as `2 : Int *`. In this way, the [Num] type makes it possible to have `1 + 0x1` return `2 : Int *` and `1.5 + 1.5` return `3.0 : Frac`. ## Number Literals Number literals without decimal points (like `0`, `4` or `360`) have the type `Num *` at first, but usually end up taking on a more specific type based on how they're used. For example, in `1 + List.len(my_list)`, the `1` has the type `Num *` at first, but because `List.len` returns a `U64`, the `1` ends up changing from `Num *` to the more specific `U64`, and the expression as a whole ends up having the type `U64`. Sometimes number literals don't become more specific. For example, the `Num.to_str` function has the type `Num * -> Str`. This means that when calling `Num.to_str(5 + 6)`, the expression `5 + 6` still has the type `Num *`. When this happens, `Num *` defaults to being an [I64] - so this addition expression would overflow if either 5 or 6 were replaced with a number big enough to cause addition overflow on an [I64] value. 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: ```roc Num.to_str(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: * `215u8` is a `215` value of type [U8] * `76.4f32` is a `76.4` value of type [F32] * `123.45dec` is a `123.45` value of type [Dec] In practice, these are rarely needed. It's most common to write number literals without any suffix. Description: A fixed-size integer - that is, a number with no fractional component. Integers come in two flavors: signed and unsigned. Signed integers can be negative ("signed" refers to how they can incorporate a minus sign), whereas unsigned integers cannot be negative. Since integers have a fixed size, the size you choose determines both the range of numbers it can represent, and also how much memory it takes up. [U8] is an an example of an integer. It is an unsigned [Int] that takes up 8 bits (aka 1 byte) in memory. The `U` is for Unsigned and the 8 is for 8 bits. Because it has 8 bits to work with, it can store 256 numbers (2^8), and because it is unsigned, its min value is 0. This means the 256 numbers it can store range from 0 to 255. [I8] is a signed integer that takes up 8 bits. The `I` is for Integer, since integers in mathematics are signed by default. Because it has 8 bits just like [U8], it can store 256 numbers (still 2^8), but because it is signed, the range is different. Its 256 numbers range from -128 to 127. Here are some other examples: * [U16] is like [U8], except it takes up 16 bits in memory. It can store 65,536 numbers (2^16), ranging from 0 to 65,536. * [I16] is like [U16], except it is signed. It can still store the same 65,536 numbers (2^16), ranging from -32,768 to 32,767. This pattern continues up to [U128] and [I128]. ## Performance Details In general, using smaller numeric sizes means your program will use less memory. However, if a mathematical operation results in an answer that is too big or too small to fit in the size available for that answer (which is typically the same size as the inputs), then you'll get an overflow error. As such, minimizing memory usage without causing overflows involves choosing number sizes based on your knowledge of what numbers you expect your program to encounter at runtime. Minimizing memory usage does not imply maximum runtime speed! CPUs are typically fastest at performing integer operations on integers that are the same size as that CPU's native machine word size. That means a 64-bit CPU is typically fastest at executing instructions on [U64] and [I64] values, whereas a 32-bit CPU is typically fastest on [U32] and [I32] values. Putting these factors together, here are some reasonable guidelines for optimizing performance through integer size choice: * Start by deciding if this integer should allow negative numbers, and choose signed or unsigned accordingly. * Next, think about the range of numbers you expect this number to hold. Choose the smallest size you will never expect to overflow, no matter the inputs your program receives. (Validating inputs for size, and presenting the user with an error if they are too big, can help guard against overflow.) * Finally, if a particular numeric calculation is running too slowly, you can try experimenting with other number sizes. This rarely makes a meaningful difference, but some processors can operate on different number sizes at different speeds. All number literals without decimal points are compatible with [Int] values. 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. ```roc 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. * *Signed* integers can be negative. Integers also come in different sizes. Choosing a size depends on your performance needs and the range of numbers you need to represent. At a high level, the general trade-offs are: * Larger integer sizes can represent a wider range of numbers. If you absolutely need to represent numbers in a certain range, make sure to pick an integer size that can hold them! * Smaller integer sizes take up less memory. These savings rarely matter in variables and function arguments, but the sizes of integers that you use in data structures can add up. This can also affect whether those data structures fit in [cache lines](https://en.wikipedia.org/wiki/CPU_cache#Cache_performance), which can be a performance bottleneck. * Certain CPUs work faster on some numeric sizes than others. If the CPU is taking too long to run numeric calculations, you may find a performance improvement by experimenting with numeric sizes that are larger than otherwise necessary. However, in practice, doing this typically degrades overall performance, so be careful to measure properly! Here are the different fixed size integer types: | Range | Type | Size | |--------------------------------------------------------|-------|----------| | ` -128` | [I8] | 1 Byte | | ` 127` | | | |--------------------------------------------------------|-------|----------| | ` 0` | [U8] | 1 Byte | | ` 255` | | | |--------------------------------------------------------|-------|----------| | ` -32_768` | [I16] | 2 Bytes | | ` 32_767` | | | |--------------------------------------------------------|-------|----------| | ` 0` | [U16] | 2 Bytes | | ` 65_535` | | | |--------------------------------------------------------|-------|----------| | ` -2_147_483_648` | [I32] | 4 Bytes | | ` 2_147_483_647` | | | |--------------------------------------------------------|-------|----------| | ` 0` | [U32] | 4 Bytes | | ` (over 4 billion) 4_294_967_295` | | | |--------------------------------------------------------|-------|----------| | ` -9_223_372_036_854_775_808` | [I64] | 8 Bytes | | ` 9_223_372_036_854_775_807` | | | |--------------------------------------------------------|-------|----------| | ` 0` | [U64] | 8 Bytes | | ` (over 18 quintillion) 18_446_744_073_709_551_615` | | | |--------------------------------------------------------|-------|----------| | `-170_141_183_460_469_231_731_687_303_715_884_105_728` | [I128]| 16 Bytes | | ` 170_141_183_460_469_231_731_687_303_715_884_105_727` | | | |--------------------------------------------------------|-------|----------| | ` (over 340 undecillion) 0` | [U128]| 16 Bytes | | ` 340_282_366_920_938_463_463_374_607_431_768_211_455` | | | If any operation would result in an [Int] that is either too big or too small to fit in that range (e.g. calling `Num.max_i32 + 1`), then the operation will *overflow*. When an overflow occurs, the program will crash. As such, it's very important to design your code not to exceed these bounds! If you need to do math outside these bounds, consider using a larger numeric size. Description: A fixed-size number with a fractional component. Roc fractions come in two flavors: fixed-point base-10 and floating-point base-2. * [Dec] is a 128-bit [fixed-point](https://en.wikipedia.org/wiki/Fixed-point_arithmetic) base-10 number. It's a great default choice, especially when precision is important - for example when representing currency. With [Dec], `0.1 + 0.2` returns `0.3`. [Dec] has 18 decimal places of precision and a range from `-170_141_183_460_469_231_731.687303715884105728` to `170_141_183_460_469_231_731.687303715884105727`. * [F64] and [F32] are [floating-point](https://en.wikipedia.org/wiki/Floating-point_arithmetic) base-2 numbers. They sacrifice precision for lower memory usage and improved performance on some operations. This makes them a good fit for representing graphical coordinates. With [F64], `0.1 + 0.2` returns `0.30000000000000004`. 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: ```roc was_it_precise = 0.1 + 0.2 == 0.3 ``` The value of `was_it_precise` 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: ```roc was_it_precise = 0.1f64 + 0.2 == 0.3 ``` Here, `was_it_precise` 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. The floating-point numbers ([F32] and [F64]) also have three values which are not ordinary [finite numbers](https://en.wikipedia.org/wiki/Finite_number). They are: * ∞ ([infinity](https://en.wikipedia.org/wiki/Infinity)) * -∞ (negative infinity) * *NaN* ([not a number](https://en.wikipedia.org/wiki/NaN)) These values are different from ordinary numbers in that they only occur when a floating-point calculation encounters an error. For example: * Dividing a positive [F64] by `0.0` returns ∞. * Dividing a negative [F64] by `0.0` returns -∞. * Dividing a [F64] of `0.0` by `0.0` returns [*NaN*](Num#is_nan). These rules come from the [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) floating point standard. Because almost all modern processors are built to 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. There's no literal syntax for these error values, but you can check to see if you ended up with one of them by using #is_nan, #is_finite, and #is_infinite. Whenever a function in this module could return one of these values, that possibility is noted in the function's documentation. ## Performance Details On typical modern CPUs, performance is similar between [Dec], [F64], and [F32] for addition and subtraction. For example, [F32] and [F64] do addition using a single CPU floating-point addition instruction, which typically takes a few clock cycles to complete. In contrast, [Dec] does addition using a few CPU integer arithmetic instructions, each of which typically takes only one clock cycle to complete. Exact numbers will vary by CPU, but they should be similar overall. [Dec] is significantly slower for multiplication and division. It not only needs to do more arithmetic instructions than [F32] and [F64] do, but also those instructions typically take more clock cycles to complete. With [Num.sqrt] and trigonometry functions like [Num.cos], there is an even bigger performance difference. [F32] and [F64] can do these in a single instruction, whereas [Dec] needs entire custom procedures - which use loops and conditionals. If you need to do performance-critical trigonometry or square roots, either [F64] or [F32] is probably a better choice than the usual default choice of [Dec], despite the precision problems they bring. Description: A signed 8-bit integer, ranging from -128 to 127 Description: A 64-bit [IEEE 754 binary floating-point number](https://en.wikipedia.org/wiki/IEEE_754). [F64] represents decimal numbers less precisely than [Dec] does, but operations on it can be faster because CPUs have hardware-level support for [F64] but not [Dec]. There are other tradeoffs between the two, such as: * [Dec] has a fixed number of digits it can represent before the decimal point, and a fixed number it can represent after the decimal point. In contrast, [F64]'s decimal point can "float"—which conceptually means if you don't need many digits before the decimal point, you can get more digits of precision afterwards (and vice versa). * [Dec] represents its number internally in [base-10](https://en.wikipedia.org/wiki/Decimal), whereas [F64] uses [base-2](https://en.wikipedia.org/wiki/Binary_number). This can lead to imprecise answers like `0.1 + 0.2` returning `0.3` for [Dec] and `0.30000000000000004` for [F64]. This is not a bug; rather, it's a consequence of [F64]'s base-2 representation. * [Dec] always gives a precise answer (or an error), whereas [F64] can lose precision. For example, increasing a very large [F64] number (using addition, perhaps) can result in the whole number portion being incorrect. `1234567890123456789 + 100` correctly results in a number ending in `889` for `Dec`, but results in a number ending `800` in [F64] due to precision loss. Description: A 32-bit [IEEE 754 binary floating-point number](https://en.wikipedia.org/wiki/IEEE_754). This works just like [F64] (see its docs for a comparison with [Dec]) except it's smaller. That in turn means it takes up less memory, but can store smaller numbers (and becomes imprecise more easily than [F64] does). Description: A [decimal](https://en.wikipedia.org/wiki/Decimal) number. [Dec] is a more precise way to represent decimal numbers (like currency) than [F32] and [F64] are, because [Dec] is represented in memory as base-10. In contrast, [F64] and [F32] are [base-2](https://en.wikipedia.org/wiki/Binary_number) in memory, which can lead to decimal precision loss even when doing addition and subtraction. For example, when using [F64], `0.1 + 0.2` returns 0.30000000000000004, whereas when using [Dec], `0.1 + 0.2` returns 0.3. Under the hood, a [Dec] is an [I128], and operations on it perform [base-10 fixed-point arithmetic](https://en.wikipedia.org/wiki/Fixed-point_arithmetic) with 18 decimal places of precision. This means a [Dec] can represent whole numbers up to slightly over 170 quintillion, along with 18 decimal places. (To be precise, it can store numbers between `-170_141_183_460_469_231_731.687303715884105728` and `170_141_183_460_469_231_731.687303715884105727`.) Why 18 decimal places? It's the highest number of decimal places where you can still convert any [U64] to a [Dec] without losing information. There are some use cases where [F64] and [F32] can be better choices than [Dec] despite their issues with base-10 numbers. For example, in graphical applications they can be a better choice for representing coordinates because they take up less memory, certain relevant calculations run faster (see performance details, below), and base-10 generally isn't as big a concern when dealing with screen coordinates as it is when dealing with currency. Another scenario where [F64] can be a better choice than [Dec] is when representing extremely small numbers. The smallest positive [F64] that can be represented without precision loss is 2^(−1074), which is about 5 * 10^(-324). Here is that number next to the smallest [Dec] that can be represented: * Smallest [F64]: 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005 * Smallest [Dec]: 0.000000000000000001 This is because [floating-point](https://en.wikipedia.org/wiki/Floating-point_arithmetic) numbers like [F64] can gain more digits of precision after the `.` when they aren't using as many digits before the `.` - and this applies the most if the digit before `.` is `0`. So whereas [Dec] always has 18 digits after the `.`, the number of digits after the `.` that [F32] can [F64] can represent without precision loss depends on what comes before it. ## Performance Details CPUs have dedicated instructions for many [F32] and [F64] operations, but none for [Dec]. Internally, [Dec] is represented as a 128-bit integer and uses multiple instructions to perform fractional operations. This gives [F32] and [F64] performance advantages for many operations. Here's a comparison of about how long [Dec] takes to perform a given operation compared to [F64], based on benchmarks on an [M1](https://en.wikipedia.org/wiki/Apple_M1) CPU: * [add] 0.6x * [sub] 0.6x * [mul] 15x * [div] 55x * [sin] 3.9x * [cos] 3.6x * [tan] 2.3x * [asin] 1.8x * [acos] 1.7x * [atan] 1.7x Keep in mind that arithmetic instructions are basically [the fastest thing a CPU does](http://norvig.com/21-days.html#answers), so (for example) a network request that takes 10 milliseconds to complete would go on this list as about 10000000x. So these performance differences might be more or less noticeable than the base-10 representation differences depending on the use case. e : Frac * Description: Euler's number (e) pi : Frac * Description: Archimedes' constant (π) tau : Frac * Description: Circle constant (τ) to_str : Num * -> Str Description: Convert a number to a [Str]. ```roc Num.to_str(42) ``` Only [Frac] values will include a decimal point, and they will always include one. ```roc Num.to_str(4.2) Num.to_str(4.0) ``` When this function is given a non-[finite](Num#is_finite) [F64] or [F32] value, the returned string will be `"NaN"`, `"∞"`, or `"-∞"`. int_cast : Int a -> Int b Description: Convert an [Int] to a new [Int] of the expected type: ```roc # Casts a U8 to a U16 x : U16 x = Num.int_cast(255u8) ``` In the case of downsizing, information is lost: ```roc # returns 0, as the bits were truncated. x : U8 x = Num.int_cast(256u16) ``` compare : Num a, Num a -> [ LT, EQ, GT ] is_lt : Num a, Num a -> Bool Description: Returns `Bool.true` if the first number is less than the second. `a < b` is shorthand for `Num.is_lt(a, b)`. If either argument is [*NaN*](Num#is_nan), returns `Bool.false` no matter what. (*NaN* is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).) ```roc 5 |> Num.is_lt 6 ``` is_gt : Num a, Num a -> Bool Description: Returns `Bool.true` if the first number is greater than the second. `a > b` is shorthand for `Num.is_gt a b`. If either argument is [*NaN*](Num#is_nan), returns `Bool.false` no matter what. (*NaN* is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).) ```roc Num.is_gt(6, 5) ``` is_lte : Num a, Num a -> Bool Description: Returns `Bool.true` if the first number is less than or equal to the second. `a <= b` is shorthand for `Num.is_lte(a, b)`. If either argument is [*NaN*](Num#is_nan), returns `Bool.false` no matter what. (*NaN* is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).) is_gte : Num a, Num a -> Bool Description: Returns `Bool.true` if the first number is greater than or equal to the second. `a >= b` is shorthand for `Num.is_gte(a, b)`. If either argument is [*NaN*](Num#is_nan), returns `Bool.false` no matter what. (*NaN* is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).) is_approx_eq : Frac a, Frac a, { rtol ? Frac a, atol ? Frac a } -> Bool Description: Returns `Bool.true` if the first number and second number are within a specific threshold A specific relative and absolute tolerance can be provided to change the threshold This function is symmetric: `Num.is_approx_eq(a, b) == Num.is_approx_eq(b, a)` If either argument is [*NaN*](Num#is_nan), returns `Bool.false` no matter what. (*NaN* is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).) is_zero : Num a -> Bool Description: Returns `Bool.true` if the number is `0`, and `Bool.false` otherwise. is_even : Int a -> Bool Description: A number is even if dividing it by 2 gives a remainder of 0. Examples of even numbers: 0, 2, 4, 6, 8, -2, -4, -6, -8 is_odd : Int a -> Bool Description: A number is odd if dividing it by 2 gives a remainder of 1. Examples of odd numbers: 1, 3, 5, 7, -1, -3, -5, -7 is_positive : Num a -> Bool Description: Positive numbers are greater than `0`. is_negative : Num a -> Bool Description: Negative numbers are less than `0`. to_frac : Num * -> Frac * is_nan : Frac * -> Bool Description: Returns `Bool.true` if the [Frac] is not a number as defined by [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) ```roc Num.is_nan(0 / 0) ``` is_infinite : Frac * -> Bool Description: Returns `Bool.true` if the [Frac] is positive or negative infinity as defined by [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) ```roc Num.is_infinite(1 / 0) Num.is_infinite(-1 / 0) ``` is_finite : Frac * -> Bool Description: Returns `Bool.true` if the [Frac] is not an infinity as defined by [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) ```roc Num.is_finite(42) ``` abs : Num a -> Num a Description: Returns the absolute value of the number. * For a positive number, returns the same number. * For a negative number, returns the same number except positive. * For zero, returns zero. ```roc Num.abs(4) Num.abs(-2.5) 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.min_i64] or [Num.min_i32]) will cause overflow. This is because, for any given size of signed integer (32-bit, 64-bit, etc.) its negated lowest value turns out to be 1 higher than the highest value it can represent. (For this reason, calling [Num.neg] on the lowest signed value will also cause overflow.) Calling this on an unsigned integer (like [U32] or [U64]) never does anything. abs_diff : Num a, Num a -> Num a Description: Returns the absolute difference between two numbers. ```roc Num.abs_diff(5, 3) Num.abs_diff(-3, 5) Num.abs_diff(3.0, 5.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 ∞ or -∞. For all other number types, overflow results in a panic. neg : Num a -> Num a Description: Returns a negative number when given a positive one, and vice versa. ```roc Num.neg(5) Num.neg(-2.5) Num.neg(0) Num.neg(0.0) ``` !! Num.neg is not completely implemented for all types in all contexts, see github.com/roc-lang/roc/issues/6959 You can use `\some_num -> 0 - some_num` as a workaround. 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.min_i64] or [Num.min_i32]) will cause overflow. This is because, for any given size of signed integer (32-bit, 64-bit, etc.) its negated lowest value turns out to be 1 higher than the highest value it can represent. (For this reason, calling #Num.abs on the lowest signed value will also cause overflow.) Additionally, calling #Num.neg on any unsigned integer (such as any [U64] or [U32] value) other than zero will cause overflow. (It will never crash when given a [Frac], however, because of how floating point numbers represent positive and negative numbers.) add : Num a, Num a -> Num a Description: Adds two numbers of the same type. (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)`. ```roc 5 + 7 Num.add(5, 7) ``` `Num.add` can be convenient in pipelines. ```roc 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 ∞ or -∞. For all other number types, overflow results in a panic. sub : Num a, Num a -> Num a Description: Subtracts two numbers of the same type. (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)`. ```roc 7 - 5 Num.sub(7, 5) ``` `Num.sub` can be convenient in pipelines. ```roc 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 ∞ or -∞. For all other number types, overflow results in a panic. mul : Num a, Num a -> Num a Description: Multiplies two numbers of the same type. (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)`. ```roc 5 * 7 Num.mul(5, 7) ``` `Num.mul` can be convenient in pipelines. ```roc 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 ∞ or -∞. For all other number types, overflow results in a panic. min : Num a, Num a -> Num a Description: Obtains the smaller between two numbers of the same type. ```roc Num.min(100, 0) Num.min(3.0, -3.0) ``` max : Num a, Num a -> Num a Description: Obtains the greater between two numbers of the same type. ```roc Num.max(100, 0) Num.max(3.0, -3.0) ``` sin : Frac a -> Frac a cos : Frac a -> Frac a tan : Frac a -> Frac a asin : Frac a -> Frac a acos : Frac a -> Frac a atan : Frac a -> Frac a sqrt : Frac a -> Frac a Description: Returns an approximation of the absolute value of a [Frac]'s square root. The square root of a negative number is an irrational number, and [Frac] only supports rational numbers. As such, you should make sure never to pass this function a negative number! Calling [sqrt] on a negative [Dec] will cause a panic. Calling [sqrt] on [F32] and [F64] values follows these rules: * Passing a negative [F64] or [F32] returns [*NaN*](Num#is_nan). * Passing [*NaN*](Num#is_nan) or -∞ also returns [*NaN*](Num#is_nan). * Passing ∞ returns ∞. > These rules come from the [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) > floating point standard. Because almost all modern processors are built to > 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. ```roc Num.sqrt(4.0) Num.sqrt(1.5) Num.sqrt(0.0) Num.sqrt(-4.0f64) ``` sqrt_checked : Frac a -> Result (Frac a) [SqrtOfNegative] log : Frac a -> Frac a Description: Natural logarithm log_checked : Frac a -> Result (Frac a) [LogNeedsPositive] div : Frac a, Frac a -> Frac a Description: Divides one [Frac] by another. `a / b` is shorthand for `Num.div(a, b)`. [Division by zero is undefined in mathematics](https://en.wikipedia.org/wiki/Division_by_zero). As such, you should make sure never to pass zero as the denominator to this function! Calling [div] on a [Dec] denominator of zero will cause a panic. Calling [div] on [F32] and [F64] values follows these rules: * Dividing a positive [F64] or [F32] by zero returns ∞. * Dividing a negative [F64] or [F32] by zero returns -∞. * Dividing a zero [F64] or [F32] by zero returns [*NaN*](Num#is_nan). > These rules come from the [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) > floating point standard. Because almost all modern processors are built to > 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. To divide an [Int] and a [Frac], first convert the [Int] to a [Frac] using one of the functions in this module like #to_dec. ```roc 5.0 / 7.0 Num.div(5, 7) ``` `Num.div` can be convenient in pipelines. ```roc Num.pi |> Num.div (2.0) ``` div_checked : Frac a, Frac a -> Result (Frac a) [DivByZero] div_ceil : Int a, Int a -> Int a div_ceil_checked : Int a, Int a -> Result (Int a) [DivByZero] div_trunc : Int a, Int a -> Int a Description: Divides two integers, truncating the result towards zero. `a // b` is shorthand for `Num.div_trunc(a, b)`. Division by zero is undefined in mathematics. As such, you should make sure never to pass zero as the denominator to this function! If you do, it will crash. ```roc 5 // 7 Num.div_trunc(5, 7) 8 // -3 Num.div_trunc(8, -3) ``` div_trunc_checked : Int a, Int a -> Result (Int a) [DivByZero] rem : Int a, Int a -> Int a Description: Obtains the remainder (truncating modulo) from the division of two integers. `a % b` is shorthand for `Num.rem(a, b)`. ```roc 5 % 7 Num.rem(5, 7) -8 % -3 Num.rem(-8, -3) ``` rem_checked : Int a, Int a -> Result (Int a) [DivByZero] is_multiple_of : Int a, Int a -> Bool bitwise_and : Int a, Int a -> Int a Description: Does a "bitwise and". Each bit of the output is 1 if the corresponding bit of x AND of y is 1, otherwise it's 0. bitwise_xor : Int a, Int a -> Int a Description: Does a "bitwise exclusive or". Each bit of the output is the same as the corresponding bit in x if that bit in y is 0, and it's the complement of the bit in x if that bit in y is 1. bitwise_or : Int a, Int a -> Int a Description: Does a "bitwise or". Each bit of the output is 0 if the corresponding bit of x OR of y is 0, otherwise it's 1. bitwise_not : Int a -> Int a Description: Returns the complement of x - the number you get by switching each 1 for a 0 and each 0 for a 1. This is the same as -x - 1. shift_left_by : Int a, U8 -> Int a Description: Bitwise left shift of a number by another The least significant bits always become 0. This means that shifting left is like multiplying by factors of two for unsigned integers. ```roc shift_left_by(0b0000_0011, 2) == 0b0000_1100 0b0000_0101 |> shift_left_by(2) == 0b0001_0100 ``` In some languages `shift_left_by` is implemented as a binary operator `<<`. shift_right_by : Int a, U8 -> Int a Description: Bitwise arithmetic shift of a number by another The most significant bits are copied from the current. ```roc shift_right_by(0b0000_1100, 2) == 0b0000_0011 0b0001_0100 |> shift_right_by(2) == 0b0000_0101 0b1001_0000 |> shift_right_by(2) == 0b1110_0100 ``` In some languages `shift_right_by` is implemented as a binary operator `>>>`. shift_right_zf_by : Int a, U8 -> Int a Description: Bitwise logical right shift of a number by another The most significant bits always become 0. This means that shifting right is like dividing by factors of two for unsigned integers. ```roc shift_right_zf_by(0b0010_1000, 2) == 0b0000_1010 0b0010_1000 |> shift_right_zf_by(2) == 0b0000_1010 0b1001_0000 |> shift_right_zf_by(2) == 0b0010_0100 ``` In some languages `shift_right_zf_by` is implemented as a binary operator `>>`. round : Frac * -> Int * Description: Round off the given fraction to the nearest integer. floor : Frac * -> Int * ceiling : Frac * -> Int * pow : Frac a, Frac a -> Frac a Description: Raises a [Frac] to the power of another [Frac]. For an [Int] alternative to this function, see [Num.pow_int] pow_int : Int a, Int a -> Int a Description: Raises an integer to the power of another, by multiplying the integer by itself the given number of times. This process is known as [exponentiation by squaring](https://en.wikipedia.org/wiki/Exponentiation_by_squaring). For a [Frac] alternative to this function, which supports negative exponents, see #Num.pow. ## Warning It is very easy for this function to produce an answer so large it causes an overflow. count_leading_zero_bits : Int a -> U8 Description: Counts the number of most-significant (leading in a big-Endian sense) zeroes in an integer. ```roc Num.count_leading_zero_bits(0b0001_1100u8) 3 Num.count_leading_zero_bits(0b0000_0000u8) 8 ``` count_trailing_zero_bits : Int a -> U8 Description: Counts the number of least-significant (trailing in a big-Endian sense) zeroes in an integer. ```roc Num.count_trailing_zero_bits(0b0001_1100u8) 2 Num.count_trailing_zero_bits(0b0000_0000u8) 8 ``` count_one_bits : Int a -> U8 Description: Counts the number of set bits in an integer. ```roc Num.count_one_bits(0b0001_1100u8) 3 Num.count_one_bits(0b0000_0000u8) 0 ``` add_wrap : Int range, Int range -> Int range add_saturated : Num a, Num a -> Num a Description: Adds two numbers, clamping on the maximum representable number rather than overflowing. This is the same as [Num.add] except for the saturating behavior if the addition is to overflow. For example, if `x : U8` is 200 and `y : U8` is 100, `add_saturated x y` will yield 255, the maximum value of a `U8`. add_checked : Num a, Num a -> Result (Num a) [Overflow] Description: Adds two numbers and checks for overflow. This is the same as [Num.add] except if the operation overflows, instead of panicking or returning ∞ or -∞, it will return `Err Overflow`. sub_wrap : Int range, Int range -> Int range sub_saturated : Num a, Num a -> Num a Description: Subtracts two numbers, clamping on the minimum representable number rather than overflowing. This is the same as [Num.sub] except for the saturating behavior if the subtraction is to overflow. For example, if `x : U8` is 10 and `y : U8` is 20, `sub_saturated x y` will yield 0, the minimum value of a `U8`. sub_checked : Num a, Num a -> Result (Num a) [Overflow] Description: Subtracts two numbers and checks for overflow. This is the same as [Num.sub] except if the operation overflows, instead of panicking or returning ∞ or -∞, it will return `Err Overflow`. mul_wrap : Int range, Int range -> Int range mul_saturated : Num a, Num a -> Num a Description: Multiplies two numbers, clamping on the maximum representable number rather than overflowing. This is the same as [Num.mul] except for the saturating behavior if the addition is to overflow. mul_checked : Num a, Num a -> Result (Num a) [Overflow] Description: Multiplies two numbers and checks for overflow. This is the same as [Num.mul] except if the operation overflows, instead of panicking or returning ∞ or -∞, it will return `Err Overflow`. min_i8 : I8 Description: Returns the lowest number that can be stored in an [I8] without underflowing its available memory and crashing. For reference, this number is `-128`. Note that the positive version of this number is larger than [Num.max_i8], which means if you call [Num.abs] on [Num.min_i8], it will overflow and crash! max_i8 : I8 Description: Returns the highest number that can be stored in an [I8] without overflowing its available memory and crashing. For reference, this number is `127`. Note that this is smaller than the positive version of [Num.min_i8], which means if you call [Num.abs] on [Num.min_i8], it will overflow and crash! min_u8 : U8 Description: Returns the lowest number that can be stored in a [U8] without underflowing its available memory and crashing. For reference, this number is zero, because [U8] is [unsigned](https://en.wikipedia.org/wiki/Signed_number_representations), and zero is the lowest unsigned number. Unsigned numbers cannot be negative. max_u8 : U8 Description: Returns the highest number that can be stored in a [U8] without overflowing its available memory and crashing. For reference, this number is `255`. min_i16 : I16 Description: Returns the lowest number that can be stored in an [I16] without underflowing its available memory and crashing. For reference, this number is `-32_768`. Note that the positive version of this number is larger than [Num.max_i16], which means if you call [Num.abs] on [Num.min_i16], it will overflow and crash! max_i16 : I16 Description: Returns the highest number that can be stored in an [I16] without overflowing its available memory and crashing. For reference, this number is `32_767`. Note that this is smaller than the positive version of [Num.min_i16], which means if you call [Num.abs] on [Num.min_i16], it will overflow and crash! min_u16 : U16 Description: Returns the lowest number that can be stored in a [U16] without underflowing its available memory and crashing. For reference, this number is zero, because [U16] is [unsigned](https://en.wikipedia.org/wiki/Signed_number_representations), and zero is the lowest unsigned number. Unsigned numbers cannot be negative. max_u16 : U16 Description: Returns the highest number that can be stored in a [U16] without overflowing its available memory and crashing. For reference, this number is `65_535`. min_i32 : I32 Description: Returns the lowest number that can be stored in an [I32] without underflowing its available memory and crashing. For reference, this number is `-2_147_483_648`. Note that the positive version of this number is larger than [Num.max_i32], which means if you call [Num.abs] on [Num.min_i32], it will overflow and crash! max_i32 : I32 Description: Returns the highest number that can be stored in an [I32] without overflowing its available memory and crashing. For reference, this number is `2_147_483_647`, which is over 2 million. Note that this is smaller than the positive version of [Num.min_i32], which means if you call [Num.abs] on [Num.min_i32], it will overflow and crash! min_u32 : U32 Description: Returns the lowest number that can be stored in a [U32] without underflowing its available memory and crashing. For reference, this number is zero, because [U32] is [unsigned](https://en.wikipedia.org/wiki/Signed_number_representations), and zero is the lowest unsigned number. Unsigned numbers cannot be negative. max_u32 : U32 Description: Returns the highest number that can be stored in a [U32] without overflowing its available memory and crashing. For reference, this number is `4_294_967_295`. min_i64 : I64 Description: Returns the lowest number that can be stored in an [I64] without underflowing its available memory and crashing. For reference, this number is `-9_223_372_036_854_775_808`, which is under 9 quintillion. Note that the positive version of this number is larger than [Num.max_i64], which means if you call [Num.abs] on [Num.min_i64], it will overflow and crash! max_i64 : I64 Description: Returns the highest number that can be stored in an [I64] without overflowing its available memory and crashing. For reference, this number is `9_223_372_036_854_775_807`, which is over 9 quintillion. Note that this is smaller than the positive version of [Num.min_i64], which means if you call [Num.abs] on [Num.min_i64], it will overflow and crash! min_u64 : U64 Description: Returns the lowest number that can be stored in a [U64] without underflowing its available memory and crashing. For reference, this number is zero, because [U64] is [unsigned](https://en.wikipedia.org/wiki/Signed_number_representations), and zero is the lowest unsigned number. Unsigned numbers cannot be negative. max_u64 : U64 Description: Returns the highest number that can be stored in a [U64] without overflowing its available memory and crashing. For reference, this number is `18_446_744_073_709_551_615`, which is over 18 quintillion. min_i128 : I128 Description: Returns the lowest number that can be stored in an [I128] without underflowing its available memory and crashing. For reference, this number is `-170_141_183_460_469_231_731_687_303_715_884_105_728`. which is under 170 undecillion. Note that the positive version of this number is larger than [Num.max_i128], which means if you call [Num.abs] on [Num.min_i128], it will overflow and crash! max_i128 : I128 Description: Returns the highest number that can be stored in an [I128] without overflowing its available memory and crashing. For reference, this number is `170_141_183_460_469_231_731_687_303_715_884_105_727`, which is over 170 undecillion. Note that this is smaller than the positive version of [Num.min_i128], which means if you call [Num.abs] on [Num.min_i128], it will overflow and crash! min_u128 : U128 Description: Returns the lowest number that can be stored in a [U128] without underflowing its available memory and crashing. For reference, this number is zero, because [U128] is [unsigned](https://en.wikipedia.org/wiki/Signed_number_representations), and zero is the lowest unsigned number. Unsigned numbers cannot be negative. max_u128 : U128 Description: Returns the highest number that can be stored in a [U128] without overflowing its available memory and crashing. For reference, this number is `340_282_366_920_938_463_463_374_607_431_768_211_455`, which is over 340 undecillion. min_f32 : F32 max_f32 : F32 min_f64 : F64 max_f64 : F64 to_i8 : Int * -> I8 Description: Converts an [Int] to an [I8]. If the given number can't be precisely represented in an [I8], the returned number may be different from the given number. to_i16 : Int * -> I16 to_i32 : Int * -> I32 to_i64 : Int * -> I64 to_i128 : Int * -> I128 to_u8 : Int * -> U8 to_u16 : Int * -> U16 to_u32 : Int * -> U32 to_u64 : Int * -> U64 to_u128 : Int * -> U128 to_f32 : Num * -> F32 Description: Converts a [Num] to an [F32]. If the given number can't be precisely represented in an [F32], the returned number may be different from the given number. to_f64 : Num * -> F64 Description: Converts a [Num] to an [F64]. If the given number can't be precisely represented in an [F64], the returned number may be different from the given number. to_i8_checked : Int * -> Result I8 [OutOfBounds] Description: Converts a [Int] to an [I8]. If the given integer can't be precisely represented in an [I8], returns `Err OutOfBounds`. to_i16_checked : Int * -> Result I16 [OutOfBounds] to_i32_checked : Int * -> Result I32 [OutOfBounds] to_i64_checked : Int * -> Result I64 [OutOfBounds] to_i128_checked : Int * -> Result I128 [OutOfBounds] to_u8_checked : Int * -> Result U8 [OutOfBounds] to_u16_checked : Int * -> Result U16 [OutOfBounds] to_u32_checked : Int * -> Result U32 [OutOfBounds] to_u64_checked : Int * -> Result U64 [OutOfBounds] to_u128_checked : Int * -> Result U128 [OutOfBounds] to_f32_checked : Num * -> Result F32 [OutOfBounds] to_f64_checked : Num * -> Result F64 [OutOfBounds] without_decimal_point : Dec -> I128 Description: Turns a [Dec] into its [I128] representation by removing the decimal point. This is equivalent to multiplying the [Dec] by 10^18. with_decimal_point : I128 -> Dec Description: Turns a [I128] into the coresponding [Dec] by adding the decimal point. This is equivalent to dividing the [I128] by 10^18. f32_to_parts : F32 -> { sign : Bool, exponent : U8, fraction : U32 } Description: Splits a [F32] into its components according to IEEE 754 standard. This function is deprecated due to common pitfalls when working with float components using Roc’s fixed-size integer types. Please use [Num.f32_to_bits] instead and extract the [IEEE 754 parts](https://en.wikipedia.org/wiki/Single-precision_floating-point_format#IEEE_754_standard:_binary32) manually from the resulting [U32]. f64_to_parts : F64 -> { sign : Bool, exponent : U16, fraction : U64 } Description: Splits a [F64] into its components according to IEEE 754 standard. This function is deprecated due to common pitfalls when working with float components using Roc’s fixed-size integer types. Please use [Num.f64_to_bits] instead and extract the [IEEE 754 parts](https://en.wikipedia.org/wiki/Double-precision_floating-point_format#IEEE_754_double-precision_binary_floating-point_format:_binary64) manually from the resulting [U64]. f32_from_parts : { sign : Bool, exponent : U8, fraction : U32 } -> F32 Description: Combine parts of a [F32] according to IEEE 754 standard. The fraction should not be bigger than 0x007F_FFFF, any bigger value will be truncated. This function is deprecated due to common pitfalls when working with float components using Roc’s fixed-size integer types. Please combine the [IEEE 754 parts](https://en.wikipedia.org/wiki/Single-precision_floating-point_format#IEEE_754_standard:_binary32) manually into a [U32] and use [Num.f32_from_bits] to create the [F32] instead. f64_from_parts : { sign : Bool, exponent : U16, fraction : U64 } -> F64 Description: Combine parts of a [F64] according to IEEE 754 standard. The fraction should not be bigger than 0x000F_FFFF_FFFF_FFFF, any bigger value will be truncated. The exponent should not be bigger than 0x07FF, any bigger value will be truncated. This function is deprecated due to common pitfalls when working with float components using Roc’s fixed-size integer types. Please combine the [IEEE 754 parts](https://en.wikipedia.org/wiki/Double-precision_floating-point_format#IEEE_754_double-precision_binary_floating-point_format:_binary64) manually into a [U64] and use [Num.f64_from_bits] to create the [F64] instead. f32_to_bits : F32 -> U32 Description: Returns a [U32] containing the raw bits of a [F32], as defined by the IEEE 754 standard. f64_to_bits : F64 -> U64 Description: Returns a [U64] containing the raw bits of a [F64], as defined by the IEEE 754 standard. dec_to_bits : Dec -> U128 Description: Returns a [U128] containing the raw bits of a [Dec], which corresponds to the raw bits of its [I128] representation. f32_from_bits : U32 -> F32 Description: Interprets a [U32] as the binary representation of a 32-bit floating-point value according to the IEEE 754 standard and returns the corresponding [F32]. f64_from_bits : U64 -> F64 Description: Interprets a [U64] as the binary representation of a 64-bit floating-point value according to the IEEE 754 standard and returns the corresponding [F64]. dec_from_bits : U128 -> Dec Description: Interprets a [U128] as the raw bits of the internal [I128] representation of a fixed-point decimal value and returns the corresponding [Dec]. from_bool : Bool -> Num * Description: Convert a `Bool` to a `Num` ```roc expect Num.from_bool(Bool.true) == 1 expect Num.from_bool(Bool.false) == 0 ``` nan_f32 : F32 Description: The value for not-a-number for a [F32] according to the IEEE 754 standard. nan_f64 : F64 Description: The value for not-a-number for a [F64] according to the IEEE 754 standard. infinity_f32 : F32 Description: The value for infinity for a [F32] according to the IEEE 754 standard. infinity_f64 : F64 Description: The value for infinity for a [F64] according to the IEEE 754 standard. ### Bool Eq : implements is_eq : a, a -> Bool where a implements Eq Description: Defines a type that can be compared for total equality. Total equality means that all values of the type can be compared to each other, and two values `a`, `b` are identical if and only if `is_eq(a, b)` is `Bool.true`. Not all types support total equality. For example, [`F32`](Num#F32) and [`F64`](Num#F64) can be a `NaN` ([Not a Number](https://en.wikipedia.org/wiki/NaN)), and the [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) floating point standard specifies that two `NaN`s are not equal. Description: Represents the boolean true and false using an opaque type. `Bool` implements the `Eq` ability. true : Bool Description: The boolean true value. false : Bool Description: The boolean false value. not : Bool -> Bool Description: 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`. ```roc expect Bool.not(Bool.false) == Bool.true expect !Bool.false == Bool.true ``` is_not_eq : a, a -> Bool where a implements Eq Description: This will call the function `Bool.is_eq` on the inputs, and then `Bool.not` on the result. The is equivalent to the logic [XOR](https://en.wikipedia.org/wiki/Exclusive_or) gate. The infix operator `!=` can also be used as shorthand for `Bool.is_not_eq`. **Note** that `is_not_eq` does not accept arguments whose types contain functions. ```roc expect Bool.is_not_eq(Bool.false, Bool.true) == Bool.true expect (Bool.false != Bool.false) == Bool.false expect "Apples" != "Oranges" ``` ### Result Result : [ Ok ok, Err err ] Description: The result of an operation that could fail: either the operation went okay, or else there was an error of some sort. is_ok : Result ok err -> Bool Description: Returns `Bool.true` if the result indicates a success, else returns `Bool.false`. ```roc Result.is_ok(Ok(5)) ``` is_err : Result ok err -> Bool Description: Returns `Bool.true` if the result indicates a failure, else returns `Bool.false`. ```roc Result.is_err(Err("uh oh")) ``` with_default : Result ok err, ok -> ok Description: If the result is `Ok`, returns the value it holds. Otherwise, returns the given default value. Note: This function should be used sparingly, because it hides that an error happened, which will make debugging harder. Prefer using `?` to forward errors or handle them explicitly with `when`. ```roc Result.with_default(Err("uh oh"), 42) # = 42 Result.with_default(Ok(7), 42) # = 7 ``` map_ok : Result a err, (a -> b) -> Result b err Description: If the result is `Ok`, transforms the value it holds by running a conversion function on it. Then returns a new `Ok` holding the transformed value. If the result is `Err`, this has no effect. Use [map_err] to transform an `Err`. ```roc Result.map_ok(Ok(12), Num.neg) # = Ok(-12) Result.map_ok(Err("yipes!"), Num.neg) # = Err("yipes!") ``` Functions like `map` are common in Roc; see for example [List.map], `Set.map`, and `Dict.map`. map_err : Result ok a, (a -> b) -> Result ok b Description: If the result is `Err`, transforms the value it holds by running a conversion function on it. Then returns a new `Err` holding the transformed value. If the result is `Ok`, this has no effect. Use [map_ok] to transform an `Ok`. ```roc List.last([]) |> Result.map_err(|_| ProvidedListIsEmpty) # = Err(ProvidedListIsEmpty) List.last([4]) |> Result.map_err(|_| ProvidedListIsEmpty) # = Ok(4) ``` on_err : Result a err, (err -> Result a other_err) -> Result a other_err Description: If the result is `Err`, transforms the entire result by running a conversion function on the value the `Err` holds. Then returns that new result. If the result is `Ok`, this has no effect. Use `?` or [try] to transform an `Ok`. ```roc Result.on_err(Ok(10), Str.to_u64) # = Ok(10) Result.on_err(Err("42"), Str.to_u64) # = Ok(42) Result.on_err(Err("string"), Str.to_u64) # = Err(InvalidNumStr) ``` on_err! : Result a err, (err => Result a other_err) => Result a other_err Description: Like [on_err], but it allows the transformation function to produce effects. ```roc Result.on_err( Err("missing user"), |msg| Stdout.line!("ERROR: ${msg}")? Err(msg) ) ``` map_both : Result ok1 err1, (ok1 -> ok2), (err1 -> err2) -> Result ok2 err2 Description: Maps both the `Ok` and `Err` values of a `Result` to new values. map2 : Result a err, Result b err, (a, b -> c) -> Result c err Description: Maps the `Ok` values of two `Result`s to a new value using a given transformation, or returns the first `Err` value encountered. try : Result a err, (a -> Result b err) -> Result b err Description: If the result is `Ok`, transforms the entire result by running a conversion function on the value the `Ok` holds. Then returns that new result. If the result is `Err`, this has no effect. We recommend using `?` instead of `try`, it makes the code easier to read. ```roc Result.try(Ok(-1), (|num| if num < 0 then Err("negative!") else Ok(-num))) # = Err("negative!") Result.try(Err("yipes!"), (|num| -> if num < 0 then Err("negative!") else Ok(-num))) # = Err("yipes!") ``` ### List is_empty : List * -> Bool Description: Check if the list is empty. ```roc List.is_empty([1, 2, 3]) List.is_empty([]) ``` get : List a, U64 -> Result a [OutOfBounds] Description: Returns an element from a list at the given index. Returns `Err OutOfBounds` if the given index exceeds the List's length ```roc expect List.get([100, 200, 300], 1) == Ok(200) expect List.get([100, 200, 300], 5) == Err(OutOfBounds) ``` replace : List a, U64, a -> { list : List a, value : a } set : List a, U64, a -> List a Description: Replaces the element at the given index with a replacement. ```roc List.set(["a", "b", "c"], 1, "B") ``` If the given index is outside the bounds of the list, returns the original list unmodified. To drop the element at a given index, instead of replacing it, see [List.drop_at]. update : List a, U64, (a -> a) -> List a Description: Updates the element at the given index with the given function. ```roc List.update([1, 2, 3], 1, (|x| x + 1)) ``` If the given index is outside the bounds of the list, returns the original list unmodified. To replace the element at a given index, instead of updating based on the current value, see [List.set] and [List.replace] append : List a, a -> List a Description: Add a single element to the end of a list. ```roc List.append([1, 2, 3], 4) [0, 1, 2] |> List.append(3) ``` append_if_ok : List a, Result a * -> List a Description: If the given [Result] is `Ok`, add it to the end of a list. Otherwise, return the list unmodified. ```roc List.append_if_ok([1, 2, 3], Ok(4)) [0, 1, 2] |> List.append_if_ok(Err(3)) ``` prepend : List a, a -> List a Description: Add a single element to the beginning of a list. ```roc List.prepend([1, 2, 3], 0) [2, 3, 4] |> List.prepend(1) ``` prepend_if_ok : List a, Result a * -> List a Description: If the given [Result] is `Ok`, add it to the beginning of a list. Otherwise, return the list unmodified. ```roc List.prepend([1, 2, 3], Ok(0)) [2, 3, 4] |> List.prepend(Err(1)) ``` len : List * -> U64 Description: Returns the length of the list - the number of elements it contains. One [List] can store up to `Num.max_i64` elements on 64-bit targets and `Num.max_i32` on 32-bit targets like wasm. This means the #U64 this function returns can always be safely converted to #I64 or #I32, depending on the target. with_capacity : U64 -> List * Description: Create a list with space for at least capacity elements reserve : List a, U64 -> List a Description: Enlarge the list for at least capacity additional elements release_excess_capacity : List a -> List a Description: Shrink the memory footprint of a list such that it's capacity and length are equal. Note: This will also convert seamless slices to regular lists. concat : List a, List a -> List a Description: Put two lists together. ```roc List.concat([1, 2, 3], [4, 5]) [0, 1, 2] |> List.concat([3, 4]) ``` last : List a -> Result a [ListWasEmpty] Description: Returns the last element in the list, or `ListWasEmpty` if it was empty. ```roc expect List.last([1, 2, 3]) == Ok(3) expect List.last([]) == Err(ListWasEmpty) ``` single : a -> List a Description: A list with a single element in it. This is useful in pipelines, like so: ```roc websites = Str.concat(domain, ".com") |> List.single ``` repeat : a, U64 -> List a Description: Returns a list with the given length, where every element is the given value. reverse : List a -> List a Description: Returns the list with its elements reversed. ```roc expect List.reverse([1, 2, 3]) == [3, 2, 1] ``` join : List (List a) -> List a Description: Join the given lists together into one list. ```roc expect List.join([[1], [2, 3], [], [4, 5]]) == [1, 2, 3, 4, 5] expect List.join([[], []]) == [] expect List.join([]) == [] ``` contains : List a, a -> Bool where a implements Eq walk : List elem, state, (state, elem -> state) -> state Description: Build a value using each element in the list. Starting with a given `state` value, this walks through each element in the list from first to last, running a given `step` function on that element which updates the `state`. It returns the final `state` at the end. You can use it in a pipeline: ```roc [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`. Here is a table of how `state` changes as [List.walk] walks over the elements `[2, 4, 8]` using [Num.add] as its `step` function to determine the next `state`. state | elem | Num.add state elem :---: | :---: | :----------------: 0 | | 0 | 2 | 2 2 | 4 | 6 6 | 8 | 14 The following returns -6: ```roc [1, 2, 3] |> List.walk(0, Num.sub) ``` Note that in other languages, `walk` is sometimes called `reduce`, `fold`, `fold_left`, or `foldl`. walk_with_index : List elem, state, (state, elem, U64 -> state) -> state Description: Like [walk], but at each step the function also receives the index of the current element. walk_with_index_until : List elem, state, (state, elem, U64 -> [ Continue state, Break state ]) -> state Description: Like [walk_until], but at each step the function also receives the index of the current element. walk_backwards : List elem, state, (state, elem -> state) -> state Description: Note that in other languages, `walk_backwards` is sometimes called `reduce_right`, `fold`, `fold_right`, or `foldr`. walk_until : List elem, state, (state, elem -> [ Continue state, Break state ]) -> state Description: Same as [List.walk], except you can stop walking early. ## Performance Details Compared to [List.walk], this can potentially visit fewer elements (which can improve performance) at the cost of making each step take longer. However, the added cost to each step is extremely small, and can easily be outweighed if it results in skipping even a small number of elements. As such, it is typically better for performance to use this over [List.walk] if returning `Break` earlier than the last element is expected to be common. walk_backwards_until : List elem, state, (state, elem -> [ Continue state, Break state ]) -> state Description: Same as [List.walk_until], but does it from the end of the list instead. walk_from : List elem, U64, state, (state, elem -> state) -> state Description: Walks to the end of the list from a specified starting index walk_from_until : List elem, U64, state, (state, elem -> [ Continue state, Break state ]) -> state Description: A combination of [List.walk_from] and [List.walk_until] sum : List (Num a) -> Num a product : List (Num a) -> Num a any : List a, (a -> Bool) -> Bool Description: Run the given predicate on each element of the list, returning `Bool.true` if any of the elements satisfy it. all : List a, (a -> Bool) -> Bool Description: Run the given predicate on each element of the list, returning `Bool.true` if all of the elements satisfy it. keep_if : List a, (a -> Bool) -> List a Description: Run the given function on each element of a list, and return all the elements for which the function returned `Bool.true`. ```roc List.keep_if([1, 2, 3, 4], (|num| num > 2)) ``` ## Performance Details [List.keep_if] always returns a list that takes up exactly the same amount of memory as the original, even if its length decreases. This is because it can't know in advance exactly how much space it will need, and if it guesses a length that's too low, it would have to re-allocate. (If you want to do an operation like this which reduces the memory footprint of the resulting list, you can do two passes over the list with [List.walk] - one to calculate the precise new size, and another to populate the new list.) If given a unique list, [List.keep_if] will mutate it in place to assemble the appropriate list. If that happens, this function will not allocate any new memory on the heap. If all elements in the list end up being kept, Roc will return the original list unaltered. keep_if_try! : List a, (a => Result Bool err) => Result (List a) err Description: Run an effectful function that returns a [Result] on each element of a list, and return all the elements for which the function returned `Ok(Bool.true)`. ```roc dirs_only = List.keep_if_try!(path_list, Path.is_dir!)? ``` drop_if : List a, (a -> Bool) -> List a Description: Run the given function on each element of a list, and return all the elements for which the function returned `Bool.false`. ```roc List.drop_if([1, 2, 3, 4], (|num| num > 2)) ``` ## Performance Details `List.drop_if` has the same performance characteristics as [List.keep_if]. See its documentation for details on those characteristics! count_if : List a, (a -> Bool) -> U64 Description: Run the given function on each element of a list, and return the number of elements for which the function returned `Bool.true`. ```roc expect List.count_if([1, -2, -3], Num.is_negative) == 2 expect List.count_if([1, 2, 3], (|num| num > 1)) == 2 ``` keep_oks : List before, (before -> Result after *) -> List after Description: 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. ```roc expect List.keep_oks(["1", "Two", "23", "Bird"], Str.to_i32) == [1, 23] expect List.keep_oks([["a", "b"], [], ["c", "d", "e"], [] ], List.first) == ["a", "c"] fn = |str| if Str.is_empty(str) then Err(StrWasEmpty) else Ok(str) expect List.keep_oks(["", "a", "bc", "", "d", "ef", ""], fn) == ["a", "bc", "d", "ef"] ``` keep_errs : List before, (before -> Result * after) -> List after Description: 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. ```roc List.keep_errs([["a", "b"], [], [], ["c", "d", "e"]], List.last) fn = |str| if Str.is_empty(str) then Err(StrWasEmpty) else Okd(Str.len(str)) List.keep_errs(["", "a", "bc", "", "d", "ef", ""], fn) ``` map : List a, (a -> b) -> List b Description: 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. ```roc expect List.map([1, 2, 3], (|num| num + 1)) == [2, 3, 4] expect List.map(["", "a", "bc"], Str.is_empty) == [Bool.true, Bool.false, Bool.false] ``` map2 : List a, List b, (a, b -> c) -> List c Description: Run a transformation function on the first element of each list, and use that as the first element in the returned list. Repeat until a list runs out of elements. Some languages have a function named `zip`, which does something similar to calling [List.map2] passing two lists and `Pair`: ```roc zipped = List.map2(["a", "b", "c"], [1, 2, 3], Pair) ``` map3 : List a, List b, List c, (a, b, c -> d) -> List d Description: Run a transformation function on the first element of each list, and use that as the first element in the returned list. Repeat until a list runs out of elements. map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e Description: Run a transformation function on the first element of each list, and use that as the first element in the returned list. Repeat until a list runs out of elements. map_with_index : List a, (a, U64 -> b) -> List b Description: This works like [List.map], except it also passes the index of the element to the conversion function. ```roc expect List.map_with_index([10, 20, 30], (|num, index| num + index)) == [10, 21, 32] ``` Description: Returns a list of all the integers between `start` and `end`. To include the `start` and `end` integers themselves, use `At` like so: ```roc List.range({ start: At 2, end: At 5 }) == [2, 3, 4, 5] ``` To exclude them, use `After` and `Before`, like so: ```roc List.range({ start: After 2, end: Before 5 }) == [3, 4] ``` You can have the list end at a certain length rather than a certain integer: ```roc List.range({ start: At 6, end: Length 4 }) == [6, 7, 8, 9] ``` If `step` is specified, each integer increases by that much. (`step: 1` is the default.) ```roc List.range({ start: After 0, end: Before 9, step: 3 }) == [3, 6] ``` List.range will also generate a reversed list if step is negative or end comes before start: ```roc List.range({ start: At 5, end: At 2 }) == [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. sort_with : List a, (a, a -> [ LT, EQ, GT ]) -> List a Description: Sort with a custom comparison function sort_asc : List (Num a) -> List (Num a) Description: Sorts a list of numbers in ascending order (lowest to highest). To sort in descending order (highest to lowest), use [List.sort_desc] instead. sort_desc : List (Num a) -> List (Num a) Description: Sorts a list of numbers in descending order (highest to lowest). To sort in ascending order (lowest to highest), use [List.sort_asc] instead. swap : List a, U64, U64 -> List a first : List a -> Result a [ListWasEmpty] Description: Returns the first element in the list, or `ListWasEmpty` if it was empty. take_first : List elem, U64 -> List elem Description: Returns the given number of elements from the beginning of the list. ```roc List.take_first([1, 2, 3, 4, 5, 6, 7, 8], 4) == [1, 2, 3, 4] ``` If there are fewer elements in the list than the requested number, returns the entire list. ```roc List.take_first([1, 2], 5) == [1, 2] ``` To *remove* elements from the beginning of the list, use `List.take_last`. To remove elements from both the beginning and end of the list, use `List.sublist`. To split the list into two lists, use `List.split_at`. take_last : List elem, U64 -> List elem Description: Returns the given number of elements from the end of the list. ```roc List.take_last([1, 2, 3, 4, 5, 6, 7, 8], 4) == [5, 6, 7, 8] ``` If there are fewer elements in the list than the requested number, returns the entire list. ```roc List.take_last([1, 2], 5) == [1, 2] ``` To *remove* elements from the end of the list, use `List.take_first`. To remove elements from both the beginning and end of the list, use `List.sublist`. To split the list into two lists, use `List.split_at`. drop_first : List elem, U64 -> List elem Description: Drops n elements from the beginning of the list. drop_last : List elem, U64 -> List elem Description: Drops n elements from the end of the list. drop_at : List elem, U64 -> List elem Description: Drops the element at the given index from the list. This has no effect if the given index is outside the bounds of the list. To replace the element at a given index, instead of dropping it, see [List.set]. min : List (Num a) -> Result (Num a) [ListWasEmpty] max : List (Num a) -> Result (Num a) [ListWasEmpty] join_map : List a, (a -> List b) -> List b Description: Like [List.map], except the transformation function wraps the return value in a list. At the end, all the lists get joined together into one list. You may know a similar function named `concat_map` in other languages. find_first : List elem, (elem -> Bool) -> Result elem [NotFound] Description: Returns the first element of the list satisfying a predicate function. If no satisfying element is found, an `Err NotFound` is returned. find_last : List elem, (elem -> Bool) -> Result elem [NotFound] Description: Returns the last element of the list satisfying a predicate function. If no satisfying element is found, an `Err NotFound` is returned. find_first_index : List elem, (elem -> Bool) -> Result U64 [NotFound] Description: Returns the index at which the first element in the list satisfying a predicate function can be found. If no satisfying element is found, an `Err NotFound` is returned. find_last_index : List elem, (elem -> Bool) -> Result U64 [NotFound] Description: Returns the last index at which the first element in the list satisfying a predicate function can be found. If no satisfying element is found, an `Err NotFound` is returned. sublist : List elem, { start : U64, len : U64 } -> List elem Description: Returns a subsection of the given list, beginning at the `start` index and including a total of `len` elements. If `start` is outside the bounds of the given list, returns the empty list. ```roc 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. ```roc 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.take_last` can do that more efficiently. Some languages have a function called **`slice`** which works similarly to this. intersperse : List elem, elem -> List elem Description: Intersperses `sep` between the elements of `list` ```roc List.intersperse([1, 2, 3], 9) == [1, 9, 2, 9, 3] ``` starts_with : List elem, List elem -> Bool where elem implements Eq Description: Returns `Bool.true` if the first list starts with the second list. If the second list is empty, this always returns `Bool.true`; every list is considered to "start with" an empty list. If the first list is empty, this only returns `Bool.true` if the second list is empty. ends_with : List elem, List elem -> Bool where elem implements Eq Description: Returns `Bool.true` if the first list ends with the second list. If the second list is empty, this always returns `Bool.true`; every list is considered to "end with" an empty list. If the first list is empty, this only returns `Bool.true` if the second list is empty. split_at : List elem, U64 -> { before : List elem, others : List elem } Description: Splits the list into two lists, around the given index. The returned lists are labeled `before` and `others`. The `before` list will contain all the elements whose index in the original list was **less than** than the given index, # and the `others` list will be all the others. (This means if you give an index of 0, the `before` list will be empty and the `others` list will have the same elements as the original list.) split_on : List a, a -> List (List a) where a implements Eq Description: Splits the input list on the delimiter element. ```roc List.split_on([1, 2, 3], 2) == [[1], [3]] ``` split_on_list : List a, List a -> List (List a) where a implements Eq Description: Splits the input list on the delimiter list. ```roc List.split_on_list([1, 2, 3], [1, 2]) == [[], [3]] ``` split_first : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] where elem implements Eq Description: 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`. ```roc List.split_first([Foo, Z, Bar, Z, Baz], Z) == Ok({ before: [Foo], after: [Bar, Z, Baz] }) ``` split_last : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] where elem implements Eq Description: 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`. ```roc List.split_last([Foo, Z, Bar, Z, Baz], Z) == Ok({ before: [Foo, Z, Bar], after: [Baz] }) ``` chunks_of : List a, U64 -> List (List a) Description: Splits the list into many chunks, each of which is length of the given chunk size. The last chunk will be shorter if the list does not evenly divide by the chunk size. If the provided list is empty or if the chunk size is 0 then the result is an empty list. map_try : List elem, (elem -> Result ok err) -> Result (List ok) err Description: Like [List.map], except the transformation function returns a [Result]. If that function ever returns `Err`, [map_try] immediately returns that `Err`. If it returns `Ok` for every element, [map_try] returns `Ok` with the transformed list. map_try! : List elem, (elem => Result ok err) => Result (List ok) err Description: Like [List.map_try], but with an effectful function. ``` file_list = ["a.txt", "b.txt", "c.txt"] file_contents = List.map_try!(file_list, |file| File.read_utf8!(file)) ``` walk_try : List elem, state, (state, elem -> Result state err) -> Result state err Description: Same as [List.walk], except you can stop walking early by returning `Err`. ## Performance Details Compared to [List.walk], this can potentially visit fewer elements (which can improve performance) at the cost of making each step take longer. However, the added cost to each step is extremely small, and can easily be outweighed if it results in skipping even a small number of elements. As such, it is typically better for performance to use this over [List.walk] if returning `Break` earlier than the last element is expected to be common. concat_utf8 : List U8, Str -> List U8 Description: Concatenates the bytes of a string encoded as utf8 to a list of bytes. ```roc expect List.concat_utf8([1, 2, 3, 4], "🐦") == [1, 2, 3, 4, 240, 159, 144, 166] ``` for_each! : List a, (a => {}) => {} Description: Run an effectful function for each element on the list. ```roc List.for_each!(["Alice", "Bob", "Charlie"], |name| create_account!(name) log!("Account created") ) ``` If the function might fail or you need to return early, use [for_each_try!]. for_each_try! : List a, (a => Result {} err) => Result {} err Description: Run an effectful function that might fail for each element on the list. If the function returns `Err`, the iteration stops and the error is returned. ```roc List.for_each_try!(files_to_delete, |path| File.delete!(path)? Stdout.line!("${path} deleted") ) ``` walk! : List elem, state, (state, elem => state) => state Description: Build a value from the contents of a list, using an effectful function. ```roc now_multiples = List.walk!([1, 2, 3], [], |nums, i| now = Utc.now!({}) |> Utc.to_millis_since_epoch List.append(nums, now * i) ) ``` This is the same as [walk], except that the step function can have effects. walk_try! : List elem, state, (state, elem => Result state err) => Result state err Description: Build a value from the contents of a list, using an effectful function that might fail. If the function returns `Err`, the iteration stops and the error is returned. ``` names = List.walk_try!( ["First", "Middle", "Last"], [], |accumulator, which| Stdout.write!("${which} name: ")? name = Stdin.line!({})? Ok(List.append(accumulator, name)), )? ``` This is the same as [walk_try], except that the step function can have effects. ### Dict Description: A [dictionary](https://en.wikipedia.org/wiki/Associative_array) that lets you associate keys with values. ## Inserting The most basic way to use a dictionary is to start with an empty one and then: 1. Call [Dict.insert] passing a key and a value, to associate that key with that value in the dictionary. 2. Later, call [Dict.get] passing the same key as before, and it will return the value you stored. Here's an example of a dictionary which uses a city's name as the key, and its population as the associated value. ```roc population_by_city = 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 only the values. You may notice that these lists have the same order as the original insertion order. This will be true if all you ever do is [Dict.insert] and [Dict.get] operations on the dictionary, but [Dict.remove] operations can change this order. ## Removing We can remove an element from the dictionary, like so: ```roc population_by_city |> 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] does. It removes an element and moves the most recent insertion into the vacated spot. This move is done as a performance optimization, and it lets [remove] have [constant time complexity](https://en.wikipedia.org/wiki/Time_complexity#Constant_time). Dict is inspired by [IndexMap](https://docs.rs/indexmap/latest/indexmap/map/struct.IndexMap.html). The internal implementation of a dictionary is almost identical to [ankerl::unordered_dense](https://github.com/martinus/unordered_dense). It has a list of keys value pairs that is ordered based on insertion. It uses a list of indices into the data as the backing of a hash map. empty : {} -> Dict * * Description: Return an empty dictionary. ```roc empty_dict = Dict.empty({}) ``` with_capacity : U64 -> Dict * * Description: Return a dictionary with space allocated for a number of entries. This may provide a performance optimization if you know how many entries will be inserted. reserve : Dict k v, U64 -> Dict k v Description: Enlarge the dictionary for at least capacity additional elements release_excess_capacity : Dict k v -> Dict k v Description: Shrink the memory footprint of a dictionary such that capacity is as small as possible. This function will require regenerating the metadata if the size changes. There will still be some overhead due to dictionary metadata always being a power of 2. capacity : Dict * * -> U64 Description: Returns the max number of elements the dictionary can hold before requiring a rehash. ```roc food_dict = Dict.empty({}) |> Dict.insert("apple", "fruit") capacity_of_dict = Dict.capacity(food_dict) ``` single : k, v -> Dict k v Description: Returns a dictionary containing the key and value provided as input. ```roc expect Dict.single("A", "B") |> Bool.is_eq(Dict.empty({}) |> Dict.insert("A", "B")) ``` from_list : List ( k, v ) -> Dict k v Description: Returns dictionary with the keys and values specified by the input [List]. ```roc expect Dict.single(1, "One") |> Dict.insert(2, "Two") |> Dict.insert(3, "Three") |> Dict.insert(4, "Four") |> Bool.is_eq(Dict.from_list([(1, "One"), (2, "Two"), (3, "Three"), (4, "Four")])) ``` ## Performance Details This will build up from an empty dictionary to minimize totally memory use. If the list has few duplicate keys, it would be faster to allocate a dictionary with the same capacity of the list and walk it calling [Dict.insert] len : Dict * * -> U64 Description: Returns the number of values in the dictionary. ```roc expect Dict.empty({}) |> Dict.insert("One", "A Song") |> Dict.insert("Two", "Candy Canes") |> Dict.insert("Three", "Boughs of Holly") |> Dict.len |> Bool.is_eq(3) ``` is_empty : Dict * * -> Bool Description: Check if the dictionary is empty. ```roc Dict.is_empty(Dict.empty({}) |> Dict.insert("key", 42)) Dict.is_empty(Dict.empty({})) ``` clear : Dict k v -> Dict k v Description: Clears all elements from a dictionary keeping around the allocation if it isn't huge. ```roc songs = Dict.empty({}) |> Dict.insert("One", "A Song") |> Dict.insert("Two", "Candy Canes") |> Dict.insert("Three", "Boughs of Holly") clear_songs = Dict.clear(songs) expect Dict.len(clear_songs) == 0 ``` map : Dict k a, (k, a -> b) -> Dict k b Description: Convert each value in the dictionary to something new, by calling a conversion function on each of them which receives both the key and the old value. Then return a new dictionary containing the same keys and the converted values. join_map : Dict a b, (a, b -> Dict x y) -> Dict x y Description: Like [Dict.map], except the transformation function wraps the return value in a dictionary. At the end, all the dictionaries get joined together (using [Dict.insert_all]) into one dictionary. You may know a similar function named `concat_map` in other languages. walk : Dict k v, state, (state, k, v -> state) -> state Description: 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. ```roc expect Dict.empty({}) |> Dict.insert("Apples", 12) |> Dict.insert("Orange", 24) |> Dict.walk(0, (\count, _, qty -> count + qty)) |> Bool.is_eq(36) ``` walk_until : Dict k v, state, (state, k, v -> [ Continue state, Break state ]) -> state Description: Same as [Dict.walk], except you can stop walking early. ## Performance Details Compared to [Dict.walk], this can potentially visit fewer elements (which can improve performance) at the cost of making each step take longer. However, the added cost to each step is extremely small, and can easily be outweighed if it results in skipping even a small number of elements. As such, it is typically better for performance to use this over [Dict.walk] if returning `Break` earlier than the last element is expected to be common. ```roc people = Dict.empty({}) |> Dict.insert("Alice", 17) |> Dict.insert("Bob", 18) |> Dict.insert("Charlie", 19) is_adult = \_, _, age -> if age >= 18 then Break(Bool.true) else Continue(Bool.false) someone_is_an_adult = Dict.walk_until(people, Bool.false, is_adult) expect someone_is_an_adult == Bool.true ``` keep_if : Dict k v, ( ( k, v ) -> Bool) -> Dict k v Description: Run the given function on each key-value pair of a dictionary, and return a dictionary with just the pairs for which the function returned `Bool.true`. ```roc expect Dict.empty({}) |> Dict.insert("Alice", 17) |> Dict.insert("Bob", 18) |> Dict.insert("Charlie", 19) |> Dict.keep_if(\(_k, v) -> v >= 18) |> Dict.len |> Bool.is_eq(2) ``` drop_if : Dict k v, ( ( k, v ) -> Bool) -> Dict k v Description: Run the given function on each key-value pair of a dictionary, and return a dictionary with just the pairs for which the function returned `Bool.false`. ```roc expect Dict.empty({}) |> Dict.insert("Alice", 17) |> Dict.insert("Bob", 18) |> Dict.insert("Charlie", 19) |> Dict.drop_if(\(_k, v) -> v >= 18) |> Dict.len |> Bool.is_eq(1) ``` get : Dict k v, k -> Result v [KeyNotFound] Description: 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]. ```roc dictionary = Dict.empty({}) |> Dict.insert(1,s "Apple") |> Dict.insert(2,s "Orange") expect Dict.get(dictionary, 1) == Ok("Apple") expect Dict.get(dictionary, 2000) == Err(KeyNotFound) ``` contains : Dict k v, k -> Bool Description: Check if the dictionary has a value for a specified key. ```roc expect Dict.empty({}) |> Dict.insert(1234, "5678") |> Dict.contains(1234) |> Bool.is_eq(Bool.true) ``` insert : Dict k v, k, v -> Dict k v Description: Insert a value into the dictionary at a specified key. ```roc expect Dict.empty({}) |> Dict.insert("Apples", 12) |> Dict.get("Apples") |> Bool.is_eq(Ok(12)) ``` remove : Dict k v, k -> Dict k v Description: Remove a value from the dictionary for a specified key. ```roc expect Dict.empty({}) |> Dict.insert("Some", "Value") |> Dict.remove("Some") |> Dict.len |> Bool.is_eq(0) ``` update : Dict k v, k, (Result v [Missing] -> Result v [Missing]) -> Dict k v Description: Insert or remove a value for a specified key. This function enables a performance optimization 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. ```roc alter_value : Result Bool [Missing] -> Result Bool [Missing] alter_value = \possible_value -> when possible_value is Err Missing -> Ok(Bool.false) Ok value -> if value then Err(Missing) else Ok(Bool.true) expect Dict.update(Dict.empty({}), "a", alter_value) == Dict.single("a", Bool.false) expect Dict.update(Dict.single("a", Bool.false), "a", alter_value) == Dict.single("a", Bool.true) expect Dict.update(Dict.single("a", Bool.true), "a", alter_value) == Dict.empty({}) ``` to_list : Dict k v -> List ( k, v ) Description: Returns the keys and values of a dictionary as a [List]. This requires allocating a temporary list, prefer using [Dict.to_list] or [Dict.walk] instead. ```roc expect Dict.single(1, "One") |> Dict.insert(2, "Two") |> Dict.insert(3, "Three") |> Dict.insert(4, "Four") |> Dict.to_list |> Bool.is_eq([(1, "One"), (2, "Two"), (3, "Three"), (4, "Four")]) ``` keys : Dict k v -> List k Description: Returns the keys of a dictionary as a [List]. This requires allocating a temporary [List], prefer using [Dict.to_list] or [Dict.walk] instead. ```roc expect Dict.single(1, "One") |> Dict.insert(2, "Two") |> Dict.insert(3, "Three") |> Dict.insert(4, "Four") |> Dict.keys |> Bool.is_eq([1,2,3,4]) ``` values : Dict k v -> List v Description: Returns the values of a dictionary as a [List]. This requires allocating a temporary [List], prefer using [Dict.to_list] or [Dict.walk] instead. ```roc expect Dict.single(1, "One") |> Dict.insert(2, "Two") |> Dict.insert(3, "Three") |> Dict.insert(4, "Four") |> Dict.values |> Bool.is_eq(["One","Two","Three","Four"]) ``` insert_all : Dict k v, Dict k v -> Dict k v Description: Combine two dictionaries by keeping the [union](https://en.wikipedia.org/wiki/Union_(set_theory)) of all the key-value pairs. This means that all the key-value pairs in 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. ```roc 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") expected = Dict.single(1, "Keep Me") |> Dict.insert(2, "And Me") |> Dict.insert(3, "Me Too") |> Dict.insert(4, "And Also Me") expect Dict.insert_all(first, second) == expected ``` keep_shared : Dict k v, Dict k v -> Dict k v where v implements Eq Description: Combine two dictionaries by keeping the [intersection](https://en.wikipedia.org/wiki/Intersection_(set_theory)) of all the key-value pairs. This means that we keep only those pairs that are in both dictionaries. Both the key and value must match to be kept. ```roc first = Dict.single(1, "Keep Me") |> Dict.insert(2, "And Me") |> Dict.insert(3, "Not this one") second = Dict.single(1, "Keep Me") |> Dict.insert(2, "And Me") |> Dict.insert(3, "This has a different value") |> Dict.insert(4, "Or Me") expected = Dict.single(1, "Keep Me") |> Dict.insert(2, "And Me") expect Dict.keep_shared(first, second) == expected ``` remove_all : Dict k v, Dict k v -> Dict k v Description: Remove the key-value pairs in the first input that are also in the second 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. ```roc 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...") expected = Dict.single(1, "Keep Me") |> Dict.insert(2, "And Me") expect Dict.remove_all(first, second) == expected ``` ### Set Description: Provides a [set](https://en.wikipedia.org/wiki/Set_(abstract_data_type)) type which stores a collection of unique values, without any ordering empty : {} -> Set * Description: Creates a new empty `Set`. ```roc empty_set = Set.empty({}) count_values = Set.len(empty_set) expect count_values == 0 ``` with_capacity : U64 -> Set * Description: Return a set with space allocated for a number of entries. This may provide a performance optimization if you know how many entries will be inserted. reserve : Set k, U64 -> Set k Description: Enlarge the set for at least capacity additional elements release_excess_capacity : Set k -> Set k Description: Shrink the memory footprint of a set such that capacity is as small as possible. This function will require regenerating the metadata if the size changes. There will still be some overhead due to dictionary metadata always being a power of 2. single : k -> Set k Description: Creates a new `Set` with a single value. ```roc single_item_set = Set.single("Apple") count_values = Set.len(single_item_set) expect count_values == 1 ``` insert : Set k, k -> Set k Description: Insert a value into a `Set`. ```roc few_item_set = Set.empty({}) |> Set.insert("Apple") |> Set.insert("Pear") |> Set.insert("Banana") count_values = Set.len(few_item_set) expect count_values == 3 ``` len : Set * -> U64 Description: Counts the number of values in a given `Set`. ```roc few_item_set = Set.empty({}) |> Set.insert("Apple") |> Set.insert("Pear") |> Set.insert("Banana") count_values = Set.len(few_item_set) expect count_values == 3 ``` capacity : Set * -> U64 Description: Returns the max number of elements the set can hold before requiring a rehash. ```roc food_set = Set.empty({}) |> Set.insert("apple") capacity_of_set = Set.capacity(food_set) ``` is_empty : Set * -> Bool Description: Check if the set is empty. ```roc Set.is_empty(Set.empty({}) |> Set.insert(42)) Set.is_empty(Set.empty({})) ``` remove : Set k, k -> Set k Description: Removes the value from the given `Set`. ```roc numbers = Set.empty({}) |> Set.insert(10) |> Set.insert(20) |> Set.remove(10) has10 = Set.contains(numbers, 10) has20 = Set.contains(numbers, 20) expect has10 == Bool.false expect has20 == Bool.true ``` contains : Set k, k -> Bool Description: Test if a value is in the `Set`. ```roc Fruit : [Apple, Pear, Banana] fruit : Set Fruit fruit = Set.single(Apple) |> Set.insert(Pear) has_apple = Set.contains(fruit, Apple) has_banana = Set.contains(fruit, Banana) expect has_apple == Bool.true expect has_banana == Bool.false ``` to_list : Set k -> List k Description: Retrieve the values in a `Set` as a `List`. ```roc numbers : Set U64 numbers = Set.from_list([1,2,3,4,5]) values = [1,2,3,4,5] expect Set.to_list(numbers) == values ``` from_list : List k -> Set k Description: Create a `Set` from a `List` of values. ```roc values = Set.empty({}) |> Set.insert(Banana) |> Set.insert(Apple) |> Set.insert(Pear) expect Set.from_list([Pear, Apple, Banana]) == values ``` union : Set k, Set k -> Set k Description: Combine two `Set` collection by keeping the [union](https://en.wikipedia.org/wiki/Union_(set_theory)) of all the values pairs. This means that all of the values in both `Set`s will be combined. ```roc set1 = Set.single(Left) set2 = Set.single(Right) expect Set.union(set1, set2) == Set.from_list([Left, Right]) ``` intersection : Set k, Set k -> Set k Description: Combine two `Set`s by keeping the [intersection](https://en.wikipedia.org/wiki/Intersection_(set_theory)) of all the values pairs. This means that we keep only those values that are in both `Set`s. ```roc set1 = Set.from_list([Left, Other]) set2 = Set.from_list([Left, Right]) expect Set.intersection(set1, set2) == Set.single(Left) ``` difference : Set k, Set k -> Set k Description: Remove the values in the first `Set` that are also in the second `Set` 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 values that are in the first and not in the second. ```roc first = Set.from_list([Left, Right, Up, Down]) second = Set.from_list([Left, Right]) expect Set.difference(first, second) == Set.from_list([Up, Down]) ``` walk : Set k, state, (state, k -> state) -> state Description: Iterate through the values of a given `Set` and build a value. ```roc values = Set.from_list(["March", "April", "May"]) starts_with_letter_m = \month -> when Str.to_utf8(month) is ['M', ..] -> Bool.true _ -> Bool.false reduce = \state, k -> if starts_with_letter_m(k) then state + 1 else state result = Set.walk(values, 0, reduce) expect result == 2 ``` map : Set a, (a -> b) -> Set b Description: Convert each value in the set to something new, by calling a conversion function on each of them which receives the old value. Then return a new set containing the converted values. join_map : Set a, (a -> Set b) -> Set b Description: Like [Set.map], except the transformation function wraps the return value in a set. At the end, all the sets get joined together (using [Set.union]) into one set. You may know a similar function named `concat_map` in other languages. walk_until : Set k, state, (state, k -> [ Continue state, Break state ]) -> state Description: Iterate through the values of a given `Set` and build a value, can stop iterating part way through the collection. ```roc numbers = Set.from_list([1,2,3,4,5,6,42,7,8,9,10]) find42 = \state, k -> if k == 42 then Break(FoundTheAnswer) else Continue(state) result = Set.walk_until(numbers, NotFound, find42) expect result == FoundTheAnswer ``` keep_if : Set k, (k -> Bool) -> Set k Description: Run the given function on each element in the `Set`, and return a `Set` with just the elements for which the function returned `Bool.true`. ```roc expect Set.from_list([1,2,3,4,5]) |> Set.keep_if(\k -> k >= 3) |> Bool.is_eq(Set.from_list([3,4,5])) ``` drop_if : Set k, (k -> Bool) -> Set k Description: Run the given function on each element in the `Set`, and return a `Set` with just the elements for which the function returned `Bool.false`. ```roc expect Set.from_list [1,2,3,4,5] |> Set.drop_if(\k -> k >= 3) |> Bool.is_eq(Set.from_list([1,2])) ``` ### Decode DecodeError : [TooShort] Description: Error types when decoding a `List U8` of utf-8 bytes using a [Decoder] Description: Return type of a [Decoder]. This can be useful when creating a [custom](#custom) decoder or when using [from_bytes_partial](#from_bytes_partial). For example writing unit tests, such as; ```roc expect input = "\"hello\", " |> Str.to_utf8 actual = Decode.from_bytes_partial(input, Json.json) expected = Ok("hello") actual.result == expected ``` Description: Decodes a `List U8` of utf-8 bytes where `val` is the type of the decoded value, and `fmt` is a [Decoder] which implements the [DecoderFormatting] ability Decoding : implements decoder : Decoder val fmt where val implements Decoding, fmt implements DecoderFormatting Description: Definition of the [Decoding] ability DecoderFormatting : implements u8 : Decoder U8 fmt where fmt implements DecoderFormatting u16 : Decoder U16 fmt where fmt implements DecoderFormatting u32 : Decoder U32 fmt where fmt implements DecoderFormatting u64 : Decoder U64 fmt where fmt implements DecoderFormatting u128 : Decoder U128 fmt where fmt implements DecoderFormatting i8 : Decoder I8 fmt where fmt implements DecoderFormatting i16 : Decoder I16 fmt where fmt implements DecoderFormatting i32 : Decoder I32 fmt where fmt implements DecoderFormatting i64 : Decoder I64 fmt where fmt implements DecoderFormatting i128 : Decoder I128 fmt where fmt implements DecoderFormatting f32 : Decoder F32 fmt where fmt implements DecoderFormatting f64 : Decoder F64 fmt where fmt implements DecoderFormatting dec : Decoder Dec fmt where fmt implements DecoderFormatting bool : Decoder Bool fmt where fmt implements DecoderFormatting string : Decoder Str fmt where fmt implements DecoderFormatting list : Decoder elem fmt -> Decoder (List elem) fmt where fmt implements DecoderFormatting record : state, (state, Str -> [ Keep (Decoder state fmt), Skip ]), (state, fmt -> Result val DecodeError) -> Decoder val fmt where fmt implements DecoderFormatting tuple : state, (state, U64 -> [ Next (Decoder state fmt), TooLong ]), (state -> Result val DecodeError) -> Decoder val fmt where fmt implements DecoderFormatting Description: Definition of the [DecoderFormatting] ability custom : (List U8, fmt -> DecodeResult val) -> Decoder val fmt where fmt implements DecoderFormatting Description: Build a custom [Decoder] function. For example the implementation of `decode_bool` could be defined as follows; ```roc decode_bool = Decode.custom \bytes, @Json({}) -> when bytes is ['f', 'a', 'l', 's', 'e', ..] -> { result: Ok(Bool.false), rest: List.drop_first(bytes, 5) } ['t', 'r', 'u', 'e', ..] -> { result: Ok Bool.true, rest: List.drop_first(bytes, 4) } _ -> { result: Err(TooShort), rest: bytes } ``` decode_with : List U8, Decoder val fmt, fmt -> DecodeResult val where fmt implements DecoderFormatting Description: Decode a `List U8` utf-8 bytes using a specific [Decoder] function from_bytes_partial : List U8, fmt -> DecodeResult val where val implements Decoding, fmt implements DecoderFormatting Description: Decode a `List U8` utf-8 bytes and return a [DecodeResult](#DecodeResult) ```roc expect input = "\"hello\", " |> Str.to_utf8 actual = Decode.from_bytes_partial(input Json.json) expected = Ok("hello") actual.result == expected ``` from_bytes : List U8, fmt -> Result val [Leftover (List U8)]DecodeError where val implements Decoding, fmt implements DecoderFormatting Description: Decode a `List U8` utf-8 bytes and return a [Result] with no leftover bytes expected. If successful returns `Ok val`, however, if there are bytes remaining returns `Err Leftover (List U8)`. ```roc expect input = "\"hello\", " |> Str.to_utf8 actual = Decode.from_bytes(input, Json.json) expected = Ok("hello") actual == expected ``` map_result : DecodeResult a, (a -> b) -> DecodeResult b Description: Transform the `val` of a [DecodeResult] ### Encode Encoding : implements to_encoder : val -> Encoder fmt where val implements Encoding, fmt implements EncoderFormatting EncoderFormatting : implements u8 : U8 -> Encoder fmt where fmt implements EncoderFormatting u16 : U16 -> Encoder fmt where fmt implements EncoderFormatting u32 : U32 -> Encoder fmt where fmt implements EncoderFormatting u64 : U64 -> Encoder fmt where fmt implements EncoderFormatting u128 : U128 -> Encoder fmt where fmt implements EncoderFormatting i8 : I8 -> Encoder fmt where fmt implements EncoderFormatting i16 : I16 -> Encoder fmt where fmt implements EncoderFormatting i32 : I32 -> Encoder fmt where fmt implements EncoderFormatting i64 : I64 -> Encoder fmt where fmt implements EncoderFormatting i128 : I128 -> Encoder fmt where fmt implements EncoderFormatting f32 : F32 -> Encoder fmt where fmt implements EncoderFormatting f64 : F64 -> Encoder fmt where fmt implements EncoderFormatting dec : Dec -> Encoder fmt where fmt implements EncoderFormatting bool : Bool -> Encoder fmt where fmt implements EncoderFormatting string : Str -> Encoder fmt where fmt implements EncoderFormatting list : List elem, (elem -> Encoder fmt) -> Encoder fmt where fmt implements EncoderFormatting record : List { key : Str, value : Encoder fmt } -> Encoder fmt where fmt implements EncoderFormatting tuple : List (Encoder fmt) -> Encoder fmt where fmt implements EncoderFormatting tag : Str, List (Encoder fmt) -> Encoder fmt where fmt implements EncoderFormatting custom : (List U8, fmt -> List U8) -> Encoder fmt where fmt implements EncoderFormatting Description: Creates a custom encoder from a given function. ```roc expect # Appends the byte 42 custom_encoder = Encode.custom(\bytes, _fmt -> List.append(bytes, 42)) actual = Encode.append_with([], custom_encoder, Core.json) expected = [42] # Expected result is a list with a single byte, 42 actual == expected ``` append_with : List U8, Encoder fmt, fmt -> List U8 where fmt implements EncoderFormatting append : List U8, val, fmt -> List U8 where val implements Encoding, fmt implements EncoderFormatting Description: Appends the encoded representation of a value to an existing list of bytes. ```roc expect actual = Encode.append([], { foo: 43 }, Core.json) expected = Str.to_utf8("""{"foo":43}""") actual == expected ``` to_bytes : val, fmt -> List U8 where val implements Encoding, fmt implements EncoderFormatting Description: Encodes a value to a list of bytes (`List U8`) according to the specified format. ```roc expect foo_rec = { foo: 42 } actual = Encode.to_bytes(foo_rec, Core.json) expected = Str.to_utf8("""{"foo":42}""") actual == expected ``` ### Hash Hash : implements hash : hasher, a -> hasher where a implements Hash, hasher implements Hasher Description: A value that can be hashed. Hasher : implements add_bytes : a, List U8 -> a where a implements Hasher add_u8 : a, U8 -> a where a implements Hasher add_u16 : a, U16 -> a where a implements Hasher add_u32 : a, U32 -> a where a implements Hasher add_u64 : a, U64 -> a where a implements Hasher add_u128 : a, U128 -> a where a implements Hasher complete : a -> U64 where a implements Hasher Description: Describes a hashing algorithm that is fed bytes and produces an integer hash. The [Hasher] ability describes general-purpose hashers. It only allows emission of 64-bit unsigned integer hashes. It is not suitable for cryptographically-secure hashing. Description: Adds a string into a [Hasher] by hashing its UTF-8 bytes. Description: Adds a list of [Hash]able elements to a [Hasher] by hashing each element. hash_bool : a, Bool -> a where a implements Hasher Description: Adds a single [Bool] to a hasher. hash_i8 : a, I8 -> a where a implements Hasher Description: Adds a single I8 to a hasher. hash_i16 : a, I16 -> a where a implements Hasher Description: Adds a single I16 to a hasher. hash_i32 : a, I32 -> a where a implements Hasher Description: Adds a single I32 to a hasher. hash_i64 : a, I64 -> a where a implements Hasher Description: Adds a single I64 to a hasher. hash_i128 : a, I128 -> a where a implements Hasher Description: Adds a single I128 to a hasher. hash_dec : a, Dec -> a where a implements Hasher Description: Adds a single [Dec] to a hasher. Description: Adds a container of [Hash]able elements to a [Hasher] by hashing each element. The container is iterated using the walk method passed in. The order of the elements does not affect the final hash. ### Box box : a -> Box a Description: Allocates a value on the heap. Boxing is an expensive process as it copies 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. ```roc expect Box.unbox(Box.box("Stack Faster")) == "Stack Faster" ``` unbox : Box a -> a Description: Returns a boxed value. ```roc expect Box.unbox(Box.box("Stack Faster") == "Stack Faster" ``` ### Inspect KeyValWalker : collection, state, (state, key, val -> state) -> state ElemWalker : collection, state, (state, elem -> state) -> state InspectFormatter : implements init : {} -> f where f implements InspectFormatter tag : Str, List (Inspector f) -> Inspector f where f implements InspectFormatter tuple : List (Inspector f) -> Inspector f where f implements InspectFormatter record : List { key : Str, value : Inspector f } -> Inspector f where f implements InspectFormatter bool : Bool -> Inspector f where f implements InspectFormatter str : Str -> Inspector f where f implements InspectFormatter list : list, ElemWalker state list elem, (elem -> Inspector f) -> Inspector f where f implements InspectFormatter set : set, ElemWalker state set elem, (elem -> Inspector f) -> Inspector f where f implements InspectFormatter dict : dict, KeyValWalker state dict key value, (key -> Inspector f), (value -> Inspector f) -> Inspector f where f implements InspectFormatter opaque : * -> Inspector f where f implements InspectFormatter function : * -> Inspector f where f implements InspectFormatter u8 : U8 -> Inspector f where f implements InspectFormatter i8 : I8 -> Inspector f where f implements InspectFormatter u16 : U16 -> Inspector f where f implements InspectFormatter i16 : I16 -> Inspector f where f implements InspectFormatter u32 : U32 -> Inspector f where f implements InspectFormatter i32 : I32 -> Inspector f where f implements InspectFormatter u64 : U64 -> Inspector f where f implements InspectFormatter i64 : I64 -> Inspector f where f implements InspectFormatter u128 : U128 -> Inspector f where f implements InspectFormatter i128 : I128 -> Inspector f where f implements InspectFormatter f32 : F32 -> Inspector f where f implements InspectFormatter f64 : F64 -> Inspector f where f implements InspectFormatter dec : Dec -> Inspector f where f implements InspectFormatter custom : (f -> f) -> Inspector f where f implements InspectFormatter apply : Inspector f, f -> f where f implements InspectFormatter Inspect : implements to_inspector : val -> Inspector f where val implements Inspect, f implements InspectFormatter inspect : val -> f where val implements Inspect, f implements InspectFormatter to_str : val -> Str where val implements Inspect