Merge branch 'main' into fromutf-roc

This commit is contained in:
shua 2025-01-18 01:58:14 +01:00
commit 17624a9d2c
No known key found for this signature in database
161 changed files with 2323 additions and 1168 deletions

View file

@ -1,9 +1,9 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the // For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/rust // README at: https://github.com/devcontainers/templates/tree/main/src/rust
{ {
"name": "Rust", "name": "roc-devcontainer",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/rust:1-1-bullseye", "image": "roclang/devcontainer:latest",
// Use 'mounts' to make the cargo cache persistent in a Docker Volume. // Use 'mounts' to make the cargo cache persistent in a Docker Volume.
// "mounts": [ // "mounts": [
@ -15,20 +15,20 @@
// ] // ]
// Features to add to the dev container. More info: https://containers.dev/features. // Features to add to the dev container. More info: https://containers.dev/features.
"features": { // "features": {
"ghcr.io/devcontainers-contrib/features/zig:1": { // "ghcr.io/devcontainers-contrib/features/zig:1": {
"version": "0.13.0" // "version": "0.13.0"
}, // },
"ghcr.io/devcontainers-community/features/llvm:3": { // "ghcr.io/devcontainers-community/features/llvm:3": {
"version": 18 // "version": 18
} // }
}, // },
// Use 'forwardPorts' to make a list of ports inside the container available locally. // Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [], // "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created. // Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "rustc --version", // "postCreateCommand": "rustc --version",
"onCreateCommand": "sudo apt-get update && sudo apt-get install -y pkg-config libz-dev libzstd-dev valgrind" // "onCreateCommand": "sudo apt-get update && sudo apt-get install -y pkg-config libz-dev libzstd-dev valgrind"
// Configure tool-specific properties. // Configure tool-specific properties.
// "customizations": {}, // "customizations": {},

View file

@ -31,6 +31,4 @@ jobs:
run: cargo nextest-gen-llvm --release --no-fail-fast --locked -E "package(test_gen) - test(gen_str::str_append_scalar)" run: cargo nextest-gen-llvm --release --no-fail-fast --locked -E "package(test_gen) - test(gen_str::str_append_scalar)"
- name: regular rust tests - name: regular rust tests
run: cargo test --locked --release -- --skip opaque_wrap_function --skip gen_list::bool_list_literal --skip platform_switching_swift --skip swift_ui --skip gen_tags::phantom_polymorphic_record && sccache --show-stats run: cargo test --locked --release -- --skip opaque_wrap_function --skip gen_list::bool_list_literal --skip gen_tags::phantom_polymorphic_record && sccache --show-stats
# swift tests are skipped because of "Could not find or use auto-linked library 'swiftCompatibilityConcurrency'" on macos x86_64 CI machine
# this issue may be caused by using older versions of XCode

View file

@ -27,9 +27,7 @@ jobs:
run: ./ci/write_version.sh run: ./ci/write_version.sh
- name: execute rust tests - name: execute rust tests
run: cargo test --release --locked -- --skip opaque_wrap_function --skip gen_list::bool_list_literal --skip platform_switching_swift --skip swift_ui --skip gen_tags::phantom_polymorphic_record run: cargo test --release --locked -- --skip opaque_wrap_function --skip gen_list::bool_list_literal --skip gen_tags::phantom_polymorphic_record
# swift tests are skipped because of "Could not find or use auto-linked library 'swiftCompatibilityConcurrency'" on macos x86_64 CI machine
# this issue may be caused by using older versions of XCode
- name: build release - name: build release
run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --profile=release-with-lto --locked --bin roc --bin roc_language_server run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --profile=release-with-lto --locked --bin roc --bin roc_language_server

View file

@ -32,7 +32,7 @@ cheapest_open = \cost_fn, model ->
) )
|> Quicksort.sort_by(.cost) |> Quicksort.sort_by(.cost)
|> List.first |> List.first
|> Result.map(.position) |> Result.map_ok(.position)
|> Result.map_err(\_ -> {}) |> Result.map_err(\_ -> {})
reconstruct_path : Dict position position, position -> List position where position implements Hash & Eq reconstruct_path : Dict position position, position -> List position where position implements Hash & Eq

View file

@ -12,7 +12,7 @@ main! = \{} ->
closure1 : {} -> Result {} [] closure1 : {} -> Result {} []
closure1 = \_ -> closure1 = \_ ->
Ok(foo(to_unit_borrowed, "a long string such that it's malloced")) Ok(foo(to_unit_borrowed, "a long string such that it's malloced"))
|> Result.map(\_ -> {}) |> Result.map_ok(\_ -> {})
to_unit_borrowed = \x -> Str.count_utf8_bytes(x) to_unit_borrowed = \x -> Str.count_utf8_bytes(x)
@ -25,8 +25,8 @@ closure2 = \_ ->
x = "a long string such that it's malloced" x = "a long string such that it's malloced"
Ok({}) Ok({})
|> Result.map(\_ -> x) |> Result.map_ok(\_ -> x)
|> Result.map(to_unit) |> Result.map_ok(to_unit)
to_unit = \_ -> {} to_unit = \_ -> {}
@ -37,7 +37,7 @@ closure3 = \_ ->
x = "a long string such that it's malloced" x = "a long string such that it's malloced"
Ok({}) Ok({})
|> Result.try(\_ -> Ok(x) |> Result.map(\_ -> {})) |> Result.try(\_ -> Ok(x) |> Result.map_ok(\_ -> {}))
# # --- # # ---
closure4 : {} -> Result {} [] closure4 : {} -> Result {} []
@ -47,4 +47,4 @@ closure4 = \_ ->
Ok({}) Ok({})
|> Result.try(\_ -> Ok(x)) |> Result.try(\_ -> Ok(x))
|> Result.map(\_ -> {}) |> Result.map_ok(\_ -> {})

View file

@ -34,7 +34,7 @@ Eq implements
## `Bool` implements the `Eq` ability. ## `Bool` implements the `Eq` ability.
Bool := [True, False] implements [Eq { is_eq: bool_is_eq }] Bool := [True, False] implements [Eq { is_eq: bool_is_eq }]
bool_is_eq = \@Bool(b1), @Bool(b2) -> structural_eq(b1, b2) bool_is_eq = |@Bool(b1), @Bool(b2)| structural_eq(b1, b2)
## The boolean true value. ## The boolean true value.
true : Bool true : Bool
@ -115,7 +115,7 @@ not : Bool -> Bool
## expect "Apples" != "Oranges" ## expect "Apples" != "Oranges"
## ``` ## ```
is_not_eq : a, a -> Bool where a implements Eq is_not_eq : a, a -> Bool where a implements Eq
is_not_eq = \a, b -> structural_not_eq(a, b) is_not_eq = |a, b| structural_not_eq(a, b)
# INTERNAL COMPILER USE ONLY: used to lower calls to `is_eq` to structural # INTERNAL COMPILER USE ONLY: used to lower calls to `is_eq` to structural
# equality via the `Eq` low-level for derived types. # equality via the `Eq` low-level for derived types.

View file

@ -123,11 +123,11 @@ DecoderFormatting implements
## _ -> { result: Err(TooShort), rest: bytes } ## _ -> { result: Err(TooShort), rest: bytes }
## ``` ## ```
custom : (List U8, fmt -> DecodeResult val) -> Decoder val fmt where fmt implements DecoderFormatting custom : (List U8, fmt -> DecodeResult val) -> Decoder val fmt where fmt implements DecoderFormatting
custom = \decode -> @Decoder(decode) custom = |decode| @Decoder(decode)
## Decode a `List U8` utf-8 bytes using a specific [Decoder] function ## Decode a `List U8` utf-8 bytes using a specific [Decoder] function
decode_with : List U8, Decoder val fmt, fmt -> DecodeResult val where fmt implements DecoderFormatting decode_with : List U8, Decoder val fmt, fmt -> DecodeResult val where fmt implements DecoderFormatting
decode_with = \bytes, @Decoder(decode), fmt -> decode(bytes, fmt) decode_with = |bytes, @Decoder(decode), fmt| decode(bytes, fmt)
## Decode a `List U8` utf-8 bytes and return a [DecodeResult](#DecodeResult) ## Decode a `List U8` utf-8 bytes and return a [DecodeResult](#DecodeResult)
## ```roc ## ```roc
@ -139,7 +139,7 @@ decode_with = \bytes, @Decoder(decode), fmt -> decode(bytes, fmt)
## actual.result == expected ## actual.result == expected
## ``` ## ```
from_bytes_partial : List U8, fmt -> DecodeResult val where val implements Decoding, fmt implements DecoderFormatting from_bytes_partial : List U8, fmt -> DecodeResult val where val implements Decoding, fmt implements DecoderFormatting
from_bytes_partial = \bytes, fmt -> decode_with(bytes, decoder, fmt) from_bytes_partial = |bytes, fmt| decode_with(bytes, decoder, fmt)
## Decode a `List U8` utf-8 bytes and return a [Result] with no leftover bytes ## 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 ## expected. If successful returns `Ok val`, however, if there are bytes
@ -153,7 +153,7 @@ from_bytes_partial = \bytes, fmt -> decode_with(bytes, decoder, fmt)
## actual == expected ## actual == expected
## ``` ## ```
from_bytes : List U8, fmt -> Result val [Leftover (List U8)]DecodeError where val implements Decoding, fmt implements DecoderFormatting from_bytes : List U8, fmt -> Result val [Leftover (List U8)]DecodeError where val implements Decoding, fmt implements DecoderFormatting
from_bytes = \bytes, fmt -> from_bytes = |bytes, fmt|
when from_bytes_partial(bytes, fmt) is when from_bytes_partial(bytes, fmt) is
{ result, rest } -> { result, rest } ->
if List.is_empty(rest) then if List.is_empty(rest) then
@ -165,4 +165,4 @@ from_bytes = \bytes, fmt ->
## Transform the `val` of a [DecodeResult] ## Transform the `val` of a [DecodeResult]
map_result : DecodeResult a, (a -> b) -> DecodeResult b map_result : DecodeResult a, (a -> b) -> DecodeResult b
map_result = \{ result, rest }, mapper -> { result: Result.map(result, mapper), rest } map_result = |{ result, rest }, mapper| { result: Result.map_ok(result, mapper), rest }

View file

@ -112,14 +112,14 @@ Dict k v := {
] ]
is_eq : Dict k v, Dict k v -> Bool where v implements Eq is_eq : Dict k v, Dict k v -> Bool where v implements Eq
is_eq = \xs, ys -> is_eq = |xs, ys|
if len(xs) != len(ys) then if len(xs) != len(ys) then
Bool.false Bool.false
else else
walk_until( walk_until(
xs, xs,
Bool.true, Bool.true,
\_, k, x_val -> |_, k, x_val|
when get(ys, k) is when get(ys, k) is
Ok(y_val) if y_val == x_val -> Ok(y_val) if y_val == x_val ->
Continue(Bool.true) Continue(Bool.true)
@ -129,12 +129,12 @@ is_eq = \xs, ys ->
) )
hash_dict : hasher, Dict k v -> hasher where v implements Hash, hasher implements Hasher hash_dict : hasher, Dict k v -> hasher where v implements Hash, hasher implements Hasher
hash_dict = \hasher, dict -> Hash.hash_unordered(hasher, to_list(dict), List.walk) hash_dict = |hasher, dict| Hash.hash_unordered(hasher, to_list(dict), List.walk)
to_inspector_dict : Dict k v -> Inspector f where k implements Inspect & Hash & Eq, v implements Inspect, f implements InspectFormatter to_inspector_dict : Dict k v -> Inspector f where k implements Inspect & Hash & Eq, v implements Inspect, f implements InspectFormatter
to_inspector_dict = \dict -> to_inspector_dict = |dict|
Inspect.custom( Inspect.custom(
\fmt -> |fmt|
Inspect.apply(Inspect.dict(dict, walk, Inspect.to_inspector, Inspect.to_inspector), fmt), Inspect.apply(Inspect.dict(dict, walk, Inspect.to_inspector, Inspect.to_inspector), fmt),
) )
@ -143,7 +143,7 @@ to_inspector_dict = \dict ->
## empty_dict = Dict.empty({}) ## empty_dict = Dict.empty({})
## ``` ## ```
empty : {} -> Dict * * empty : {} -> Dict * *
empty = \{} -> empty = |{}|
@Dict( @Dict(
{ {
buckets: [], buckets: [],
@ -158,13 +158,13 @@ empty = \{} ->
## may provide a performance optimization if you know how many entries will be ## may provide a performance optimization if you know how many entries will be
## inserted. ## inserted.
with_capacity : U64 -> Dict * * with_capacity : U64 -> Dict * *
with_capacity = \requested -> with_capacity = |requested|
empty({}) empty({})
|> reserve(requested) |> reserve(requested)
## Enlarge the dictionary for at least capacity additional elements ## Enlarge the dictionary for at least capacity additional elements
reserve : Dict k v, U64 -> Dict k v reserve : Dict k v, U64 -> Dict k v
reserve = \@Dict({ buckets, data, max_bucket_capacity: original_max_bucket_capacity, max_load_factor, shifts }), requested -> reserve = |@Dict({ buckets, data, max_bucket_capacity: original_max_bucket_capacity, max_load_factor, shifts }), requested|
current_size = List.len(data) current_size = List.len(data)
requested_size = Num.add_wrap(current_size, requested) requested_size = Num.add_wrap(current_size, requested)
size = Num.min(requested_size, max_size) size = Num.min(requested_size, max_size)
@ -189,7 +189,7 @@ reserve = \@Dict({ buckets, data, max_bucket_capacity: original_max_bucket_capac
## This function will require regenerating the metadata if the size changes. ## 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. ## There will still be some overhead due to dictionary metadata always being a power of 2.
release_excess_capacity : Dict k v -> Dict k v release_excess_capacity : Dict k v -> Dict k v
release_excess_capacity = \@Dict({ buckets, data, max_bucket_capacity: original_max_bucket_capacity, max_load_factor, shifts }) -> release_excess_capacity = |@Dict({ buckets, data, max_bucket_capacity: original_max_bucket_capacity, max_load_factor, shifts })|
size = List.len(data) size = List.len(data)
# NOTE: If we want, we technically could increase the load factor here to potentially minimize size more. # NOTE: If we want, we technically could increase the load factor here to potentially minimize size more.
@ -218,7 +218,7 @@ release_excess_capacity = \@Dict({ buckets, data, max_bucket_capacity: original_
## capacity_of_dict = Dict.capacity(food_dict) ## capacity_of_dict = Dict.capacity(food_dict)
## ``` ## ```
capacity : Dict * * -> U64 capacity : Dict * * -> U64
capacity = \@Dict({ max_bucket_capacity }) -> capacity = |@Dict({ max_bucket_capacity })|
max_bucket_capacity max_bucket_capacity
## Returns a dictionary containing the key and value provided as input. ## Returns a dictionary containing the key and value provided as input.
@ -228,7 +228,7 @@ capacity = \@Dict({ max_bucket_capacity }) ->
## |> Bool.is_eq(Dict.empty({}) |> Dict.insert("A", "B")) ## |> Bool.is_eq(Dict.empty({}) |> Dict.insert("A", "B"))
## ``` ## ```
single : k, v -> Dict k v single : k, v -> Dict k v
single = \k, v -> single = |k, v|
insert(empty({}), k, v) insert(empty({}), k, v)
## Returns dictionary with the keys and values specified by the input [List]. ## Returns dictionary with the keys and values specified by the input [List].
@ -247,8 +247,8 @@ single = \k, v ->
## If the list has few duplicate keys, it would be faster to allocate a dictionary ## 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] ## with the same capacity of the list and walk it calling [Dict.insert]
from_list : List (k, v) -> Dict k v from_list : List (k, v) -> Dict k v
from_list = \data -> from_list = |data|
List.walk(data, empty({}), \dict, (k, v) -> insert(dict, k, v)) List.walk(data, empty({}), |dict, (k, v)| insert(dict, k, v))
## Returns the number of values in the dictionary. ## Returns the number of values in the dictionary.
## ```roc ## ```roc
@ -261,7 +261,7 @@ from_list = \data ->
## |> Bool.is_eq(3) ## |> Bool.is_eq(3)
## ``` ## ```
len : Dict * * -> U64 len : Dict * * -> U64
len = \@Dict({ data }) -> len = |@Dict({ data })|
List.len(data) List.len(data)
## Check if the dictionary is empty. ## Check if the dictionary is empty.
@ -271,7 +271,7 @@ len = \@Dict({ data }) ->
## Dict.is_empty(Dict.empty({})) ## Dict.is_empty(Dict.empty({}))
## ``` ## ```
is_empty : Dict * * -> Bool is_empty : Dict * * -> Bool
is_empty = \@Dict({ data }) -> is_empty = |@Dict({ data })|
List.is_empty(data) List.is_empty(data)
## Clears all elements from a dictionary keeping around the allocation if it isn't huge. ## Clears all elements from a dictionary keeping around the allocation if it isn't huge.
@ -287,10 +287,10 @@ is_empty = \@Dict({ data }) ->
## expect Dict.len(clear_songs) == 0 ## expect Dict.len(clear_songs) == 0
## ``` ## ```
clear : Dict k v -> Dict k v clear : Dict k v -> Dict k v
clear = \@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }) -> clear = |@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts })|
@Dict( @Dict(
{ {
buckets: List.map(buckets, \_ -> empty_bucket), buckets: List.map(buckets, |_| empty_bucket),
# use take_first to keep around the capacity # use take_first to keep around the capacity
data: List.take_first(data, 0), data: List.take_first(data, 0),
max_bucket_capacity, max_bucket_capacity,
@ -303,13 +303,13 @@ clear = \@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts })
## function on each of them which receives both the key and the old value. Then return a ## 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. ## new dictionary containing the same keys and the converted values.
map : Dict k a, (k, a -> b) -> Dict k b map : Dict k a, (k, a -> b) -> Dict k b
map = \dict, transform -> map = |dict, transform|
init = with_capacity(capacity(dict)) init = with_capacity(capacity(dict))
walk( walk(
dict, dict,
init, init,
\answer, k, v -> |answer, k, v|
insert(answer, k, transform(k, v)), insert(answer, k, transform(k, v)),
) )
@ -319,13 +319,13 @@ map = \dict, transform ->
## ##
## You may know a similar function named `concat_map` in other languages. ## You may know a similar function named `concat_map` in other languages.
join_map : Dict a b, (a, b -> Dict x y) -> Dict x y join_map : Dict a b, (a, b -> Dict x y) -> Dict x y
join_map = \dict, transform -> join_map = |dict, transform|
init = with_capacity(capacity(dict)) # Might be a pessimization init = with_capacity(capacity(dict)) # Might be a pessimization
walk( walk(
dict, dict,
init, init,
\answer, k, v -> |answer, k, v|
insert_all(answer, transform(k, v)), insert_all(answer, transform(k, v)),
) )
@ -341,8 +341,8 @@ join_map = \dict, transform ->
## |> Bool.is_eq(36) ## |> Bool.is_eq(36)
## ``` ## ```
walk : Dict k v, state, (state, k, v -> state) -> state walk : Dict k v, state, (state, k, v -> state) -> state
walk = \@Dict({ data }), initial_state, transform -> walk = |@Dict({ data }), initial_state, transform|
List.walk(data, initial_state, \state, (k, v) -> transform(state, k, v)) List.walk(data, initial_state, |state, (k, v)| transform(state, k, v))
## Same as [Dict.walk], except you can stop walking early. ## Same as [Dict.walk], except you can stop walking early.
## ##
@ -373,8 +373,8 @@ walk = \@Dict({ data }), initial_state, transform ->
## expect someone_is_an_adult == Bool.true ## expect someone_is_an_adult == Bool.true
## ``` ## ```
walk_until : Dict k v, state, (state, k, v -> [Continue state, Break state]) -> state walk_until : Dict k v, state, (state, k, v -> [Continue state, Break state]) -> state
walk_until = \@Dict({ data }), initial_state, transform -> walk_until = |@Dict({ data }), initial_state, transform|
List.walk_until(data, initial_state, \state, (k, v) -> transform(state, k, v)) List.walk_until(data, initial_state, |state, (k, v)| transform(state, k, v))
## Run the given function on each key-value pair of a dictionary, and return ## 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`. ## a dictionary with just the pairs for which the function returned `Bool.true`.
@ -388,11 +388,11 @@ walk_until = \@Dict({ data }), initial_state, transform ->
## |> Bool.is_eq(2) ## |> Bool.is_eq(2)
## ``` ## ```
keep_if : Dict k v, ((k, v) -> Bool) -> Dict k v keep_if : Dict k v, ((k, v) -> Bool) -> Dict k v
keep_if = \dict, predicate -> keep_if = |dict, predicate|
keep_if_help(dict, predicate, 0, Dict.len(dict)) keep_if_help(dict, predicate, 0, Dict.len(dict))
keep_if_help : Dict k v, ((k, v) -> Bool), U64, U64 -> Dict k v keep_if_help : Dict k v, ((k, v) -> Bool), U64, U64 -> Dict k v
keep_if_help = \@Dict(dict), predicate, index, length -> keep_if_help = |@Dict(dict), predicate, index, length|
if index < length then if index < length then
(key, value) = list_get_unsafe(dict.data, index) (key, value) = list_get_unsafe(dict.data, index)
if predicate((key, value)) then if predicate((key, value)) then
@ -414,8 +414,8 @@ keep_if_help = \@Dict(dict), predicate, index, length ->
## |> Bool.is_eq(1) ## |> Bool.is_eq(1)
## ``` ## ```
drop_if : Dict k v, ((k, v) -> Bool) -> Dict k v drop_if : Dict k v, ((k, v) -> Bool) -> Dict k v
drop_if = \dict, predicate -> drop_if = |dict, predicate|
Dict.keep_if(dict, \e -> Bool.not(predicate(e))) Dict.keep_if(dict, |e| Bool.not(predicate(e)))
## Get the value for a given key. If there is a value for the specified key it ## 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]. ## will return [Ok value], otherwise return [Err KeyNotFound].
@ -429,7 +429,7 @@ drop_if = \dict, predicate ->
## expect Dict.get(dictionary, 2000) == Err(KeyNotFound) ## expect Dict.get(dictionary, 2000) == Err(KeyNotFound)
## ``` ## ```
get : Dict k v, k -> Result v [KeyNotFound] get : Dict k v, k -> Result v [KeyNotFound]
get = \dict, key -> get = |dict, key|
find(dict, key) find(dict, key)
|> .result |> .result
@ -442,7 +442,7 @@ get = \dict, key ->
## |> Bool.is_eq(Bool.true) ## |> Bool.is_eq(Bool.true)
## ``` ## ```
contains : Dict k v, k -> Bool contains : Dict k v, k -> Bool
contains = \dict, key -> contains = |dict, key|
find(dict, key) find(dict, key)
|> .result |> .result
|> Result.is_ok |> Result.is_ok
@ -456,7 +456,7 @@ contains = \dict, key ->
## |> Bool.is_eq(Ok(12)) ## |> Bool.is_eq(Ok(12))
## ``` ## ```
insert : Dict k v, k, v -> Dict k v insert : Dict k v, k, v -> Dict k v
insert = \dict, key, value -> insert = |dict, key, value|
@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }) = @Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }) =
if len(dict) < capacity(dict) then if len(dict) < capacity(dict) then
dict dict
@ -471,7 +471,7 @@ insert = \dict, key, value ->
insert_helper(buckets, data, bucket_index, dist_and_fingerprint, key, value, max_bucket_capacity, max_load_factor, shifts) insert_helper(buckets, data, bucket_index, dist_and_fingerprint, key, value, max_bucket_capacity, max_load_factor, shifts)
insert_helper : List Bucket, List (k, v), U64, U32, k, v, U64, F32, U8 -> Dict k v insert_helper : List Bucket, List (k, v), U64, U32, k, v, U64, F32, U8 -> Dict k v
insert_helper = \buckets0, data0, bucket_index0, dist_and_fingerprint0, key, value, max_bucket_capacity, max_load_factor, shifts -> insert_helper = |buckets0, data0, bucket_index0, dist_and_fingerprint0, key, value, max_bucket_capacity, max_load_factor, shifts|
loaded = list_get_unsafe(buckets0, bucket_index0) loaded = list_get_unsafe(buckets0, bucket_index0)
if dist_and_fingerprint0 == loaded.dist_and_fingerprint then if dist_and_fingerprint0 == loaded.dist_and_fingerprint then
(found_key, _) = list_get_unsafe(data0, Num.to_u64(loaded.data_index)) (found_key, _) = list_get_unsafe(data0, Num.to_u64(loaded.data_index))
@ -502,7 +502,7 @@ insert_helper = \buckets0, data0, bucket_index0, dist_and_fingerprint0, key, val
## |> Bool.is_eq(0) ## |> Bool.is_eq(0)
## ``` ## ```
remove : Dict k v, k -> Dict k v remove : Dict k v, k -> Dict k v
remove = \@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }), key -> remove = |@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }), key|
if !(List.is_empty(data)) then if !(List.is_empty(data)) then
(bucket_index0, dist_and_fingerprint0) = next_while_less(buckets, key, shifts) (bucket_index0, dist_and_fingerprint0) = next_while_less(buckets, key, shifts)
(bucket_index1, dist_and_fingerprint1) = remove_helper(buckets, bucket_index0, dist_and_fingerprint0, data, key) (bucket_index1, dist_and_fingerprint1) = remove_helper(buckets, bucket_index0, dist_and_fingerprint0, data, key)
@ -516,7 +516,7 @@ remove = \@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts })
@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }) @Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts })
remove_helper : List Bucket, U64, U32, List (k, *), k -> (U64, U32) where k implements Eq remove_helper : List Bucket, U64, U32, List (k, *), k -> (U64, U32) where k implements Eq
remove_helper = \buckets, bucket_index, dist_and_fingerprint, data, key -> remove_helper = |buckets, bucket_index, dist_and_fingerprint, data, key|
bucket = list_get_unsafe(buckets, bucket_index) bucket = list_get_unsafe(buckets, bucket_index)
if dist_and_fingerprint == bucket.dist_and_fingerprint then if dist_and_fingerprint == bucket.dist_and_fingerprint then
(found_key, _) = list_get_unsafe(data, Num.to_u64(bucket.data_index)) (found_key, _) = list_get_unsafe(data, Num.to_u64(bucket.data_index))
@ -543,7 +543,7 @@ remove_helper = \buckets, bucket_index, dist_and_fingerprint, data, key ->
## expect Dict.update(Dict.single("a", Bool.true), "a", alter_value) == Dict.empty({}) ## expect Dict.update(Dict.single("a", Bool.true), "a", alter_value) == Dict.empty({})
## ``` ## ```
update : Dict k v, k, (Result v [Missing] -> Result v [Missing]) -> Dict k v update : Dict k v, k, (Result v [Missing] -> Result v [Missing]) -> Dict k v
update = \@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }), key, alter -> update = |@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }), key, alter|
{ bucket_index, result } = find(@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }), key) { bucket_index, result } = find(@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }), key)
when result is when result is
Ok(value) -> Ok(value) ->
@ -582,7 +582,7 @@ update = \@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts })
Err(Missing) -> Err(Missing) ->
@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }) @Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts })
circular_dist = \start, end, size -> circular_dist = |start, end, size|
correction = correction =
if start > end then if start > end then
size size
@ -604,7 +604,7 @@ circular_dist = \start, end, size ->
## |> Bool.is_eq([(1, "One"), (2, "Two"), (3, "Three"), (4, "Four")]) ## |> Bool.is_eq([(1, "One"), (2, "Two"), (3, "Three"), (4, "Four")])
## ``` ## ```
to_list : Dict k v -> List (k, v) to_list : Dict k v -> List (k, v)
to_list = \@Dict({ data }) -> to_list = |@Dict({ data })|
data data
## Returns the keys of a dictionary as a [List]. ## Returns the keys of a dictionary as a [List].
@ -619,8 +619,8 @@ to_list = \@Dict({ data }) ->
## |> Bool.is_eq([1,2,3,4]) ## |> Bool.is_eq([1,2,3,4])
## ``` ## ```
keys : Dict k v -> List k keys : Dict k v -> List k
keys = \@Dict({ data }) -> keys = |@Dict({ data })|
List.map(data, \(k, _) -> k) List.map(data, |(k, _)| k)
## Returns the values of a dictionary as a [List]. ## Returns the values of a dictionary as a [List].
## This requires allocating a temporary [List], prefer using [Dict.to_list] or [Dict.walk] instead. ## This requires allocating a temporary [List], prefer using [Dict.to_list] or [Dict.walk] instead.
@ -634,8 +634,8 @@ keys = \@Dict({ data }) ->
## |> Bool.is_eq(["One","Two","Three","Four"]) ## |> Bool.is_eq(["One","Two","Three","Four"])
## ``` ## ```
values : Dict k v -> List v values : Dict k v -> List v
values = \@Dict({ data }) -> values = |@Dict({ data })|
List.map(data, \(_, v) -> v) List.map(data, |(_, v)| v)
## Combine two dictionaries by keeping the [union](https://en.wikipedia.org/wiki/Union_(set_theory)) ## 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 ## of all the key-value pairs. This means that all the key-value pairs in
@ -662,7 +662,7 @@ values = \@Dict({ data }) ->
## Dict.insert_all(first, second) == expected ## Dict.insert_all(first, second) == expected
## ``` ## ```
insert_all : Dict k v, Dict k v -> Dict k v insert_all : Dict k v, Dict k v -> Dict k v
insert_all = \xs, ys -> insert_all = |xs, ys|
if len(ys) > len(xs) then if len(ys) > len(xs) then
insert_all(ys, xs) insert_all(ys, xs)
else else
@ -690,7 +690,7 @@ insert_all = \xs, ys ->
## expect Dict.keep_shared(first, second) == expected ## expect Dict.keep_shared(first, second) == expected
## ``` ## ```
keep_shared : Dict k v, Dict k v -> Dict k v where v implements Eq keep_shared : Dict k v, Dict k v -> Dict k v where v implements Eq
keep_shared = \xs0, ys0 -> keep_shared = |xs0, ys0|
(xs1, ys1) = (xs1, ys1) =
if len(ys0) < len(xs0) then if len(ys0) < len(xs0) then
(ys0, xs0) (ys0, xs0)
@ -700,7 +700,7 @@ keep_shared = \xs0, ys0 ->
walk( walk(
xs1, xs1,
with_capacity(len(xs1)), with_capacity(len(xs1)),
\state, k, v -> |state, k, v|
when get(ys1, k) is when get(ys1, k) is
Ok(yv) if v == yv -> Ok(yv) if v == yv ->
insert(state, k, v) insert(state, k, v)
@ -730,8 +730,8 @@ keep_shared = \xs0, ys0 ->
## expect Dict.remove_all(first, second) == expected ## expect Dict.remove_all(first, second) == expected
## ``` ## ```
remove_all : Dict k v, Dict k v -> Dict k v remove_all : Dict k v, Dict k v -> Dict k v
remove_all = \xs, ys -> remove_all = |xs, ys|
walk(ys, xs, \state, k, _ -> remove(state, k)) walk(ys, xs, |state, k, _| remove(state, k))
# Below here is a list of generic helpers and internal data types for Dict # Below here is a list of generic helpers and internal data types for Dict
Bucket : { Bucket : {
@ -747,17 +747,17 @@ initial_shifts = Num.sub_wrap(64, 3) # 2^(64-shifts) number of buckets
max_size = Num.shift_left_by(1u64, 32) max_size = Num.shift_left_by(1u64, 32)
max_bucket_count = max_size max_bucket_count = max_size
increment_dist = \dist_and_fingerprint -> increment_dist = |dist_and_fingerprint|
Num.add_wrap(dist_and_fingerprint, dist_inc) Num.add_wrap(dist_and_fingerprint, dist_inc)
increment_dist_n = \dist_and_fingerprint, n -> increment_dist_n = |dist_and_fingerprint, n|
Num.add_wrap(dist_and_fingerprint, Num.mul_wrap(n, dist_inc)) Num.add_wrap(dist_and_fingerprint, Num.mul_wrap(n, dist_inc))
decrement_dist = \dist_and_fingerprint -> decrement_dist = |dist_and_fingerprint|
Num.sub_wrap(dist_and_fingerprint, dist_inc) Num.sub_wrap(dist_and_fingerprint, dist_inc)
find : Dict k v, k -> { bucket_index : U64, result : Result v [KeyNotFound] } find : Dict k v, k -> { bucket_index : U64, result : Result v [KeyNotFound] }
find = \@Dict({ buckets, data, shifts }), key -> find = |@Dict({ buckets, data, shifts }), key|
hash = hash_key(key) hash = hash_key(key)
dist_and_fingerprint = dist_and_fingerprint_from_hash(hash) dist_and_fingerprint = dist_and_fingerprint_from_hash(hash)
bucket_index = bucket_index_from_hash(hash, shifts) bucket_index = bucket_index_from_hash(hash, shifts)
@ -772,7 +772,7 @@ find = \@Dict({ buckets, data, shifts }), key ->
find_manual_unrolls = 2 find_manual_unrolls = 2
find_first_unroll : List Bucket, U64, U32, List (k, v), k -> { bucket_index : U64, result : Result v [KeyNotFound] } where k implements Eq find_first_unroll : List Bucket, U64, U32, List (k, v), k -> { bucket_index : U64, result : Result v [KeyNotFound] } where k implements Eq
find_first_unroll = \buckets, bucket_index, dist_and_fingerprint, data, key -> find_first_unroll = |buckets, bucket_index, dist_and_fingerprint, data, key|
# TODO: once we have short circuit evaluation, use it here and other similar locations in this file. # TODO: once we have short circuit evaluation, use it here and other similar locations in this file.
# Avoid the nested if with else block inconvenience. # Avoid the nested if with else block inconvenience.
bucket = list_get_unsafe(buckets, bucket_index) bucket = list_get_unsafe(buckets, bucket_index)
@ -786,7 +786,7 @@ find_first_unroll = \buckets, bucket_index, dist_and_fingerprint, data, key ->
find_second_unroll(buckets, next_bucket_index(bucket_index, List.len(buckets)), increment_dist(dist_and_fingerprint), data, key) find_second_unroll(buckets, next_bucket_index(bucket_index, List.len(buckets)), increment_dist(dist_and_fingerprint), data, key)
find_second_unroll : List Bucket, U64, U32, List (k, v), k -> { bucket_index : U64, result : Result v [KeyNotFound] } where k implements Eq find_second_unroll : List Bucket, U64, U32, List (k, v), k -> { bucket_index : U64, result : Result v [KeyNotFound] } where k implements Eq
find_second_unroll = \buckets, bucket_index, dist_and_fingerprint, data, key -> find_second_unroll = |buckets, bucket_index, dist_and_fingerprint, data, key|
bucket = list_get_unsafe(buckets, bucket_index) bucket = list_get_unsafe(buckets, bucket_index)
if dist_and_fingerprint == bucket.dist_and_fingerprint then if dist_and_fingerprint == bucket.dist_and_fingerprint then
(found_key, value) = list_get_unsafe(data, Num.to_u64(bucket.data_index)) (found_key, value) = list_get_unsafe(data, Num.to_u64(bucket.data_index))
@ -798,7 +798,7 @@ find_second_unroll = \buckets, bucket_index, dist_and_fingerprint, data, key ->
find_helper(buckets, next_bucket_index(bucket_index, List.len(buckets)), increment_dist(dist_and_fingerprint), data, key) find_helper(buckets, next_bucket_index(bucket_index, List.len(buckets)), increment_dist(dist_and_fingerprint), data, key)
find_helper : List Bucket, U64, U32, List (k, v), k -> { bucket_index : U64, result : Result v [KeyNotFound] } where k implements Eq find_helper : List Bucket, U64, U32, List (k, v), k -> { bucket_index : U64, result : Result v [KeyNotFound] } where k implements Eq
find_helper = \buckets, bucket_index, dist_and_fingerprint, data, key -> find_helper = |buckets, bucket_index, dist_and_fingerprint, data, key|
bucket = list_get_unsafe(buckets, bucket_index) bucket = list_get_unsafe(buckets, bucket_index)
if dist_and_fingerprint == bucket.dist_and_fingerprint then if dist_and_fingerprint == bucket.dist_and_fingerprint then
(found_key, value) = list_get_unsafe(data, Num.to_u64(bucket.data_index)) (found_key, value) = list_get_unsafe(data, Num.to_u64(bucket.data_index))
@ -812,7 +812,7 @@ find_helper = \buckets, bucket_index, dist_and_fingerprint, data, key ->
find_helper(buckets, next_bucket_index(bucket_index, List.len(buckets)), increment_dist(dist_and_fingerprint), data, key) find_helper(buckets, next_bucket_index(bucket_index, List.len(buckets)), increment_dist(dist_and_fingerprint), data, key)
remove_bucket : Dict k v, U64 -> Dict k v remove_bucket : Dict k v, U64 -> Dict k v
remove_bucket = \@Dict({ buckets: buckets0, data: data0, max_bucket_capacity, max_load_factor, shifts }), bucket_index0 -> remove_bucket = |@Dict({ buckets: buckets0, data: data0, max_bucket_capacity, max_load_factor, shifts }), bucket_index0|
data_index_to_remove = list_get_unsafe(buckets0, bucket_index0) |> .data_index data_index_to_remove = list_get_unsafe(buckets0, bucket_index0) |> .data_index
data_index_to_remove_u64 = Num.to_u64(data_index_to_remove) data_index_to_remove_u64 = Num.to_u64(data_index_to_remove)
@ -853,7 +853,7 @@ remove_bucket = \@Dict({ buckets: buckets0, data: data0, max_bucket_capacity, ma
) )
scan_for_index : List Bucket, U64, U32 -> U64 scan_for_index : List Bucket, U64, U32 -> U64
scan_for_index = \buckets, bucket_index, data_index -> scan_for_index = |buckets, bucket_index, data_index|
bucket = list_get_unsafe(buckets, bucket_index) bucket = list_get_unsafe(buckets, bucket_index)
if bucket.data_index != data_index then if bucket.data_index != data_index then
scan_for_index(buckets, next_bucket_index(bucket_index, List.len(buckets)), data_index) scan_for_index(buckets, next_bucket_index(bucket_index, List.len(buckets)), data_index)
@ -861,7 +861,7 @@ scan_for_index = \buckets, bucket_index, data_index ->
bucket_index bucket_index
remove_bucket_helper : List Bucket, U64 -> (List Bucket, U64) remove_bucket_helper : List Bucket, U64 -> (List Bucket, U64)
remove_bucket_helper = \buckets, bucket_index -> remove_bucket_helper = |buckets, bucket_index|
next_index = next_bucket_index(bucket_index, List.len(buckets)) next_index = next_bucket_index(bucket_index, List.len(buckets))
next_bucket = list_get_unsafe(buckets, next_index) next_bucket = list_get_unsafe(buckets, next_index)
# shift down until either empty or an element with correct spot is found # shift down until either empty or an element with correct spot is found
@ -872,7 +872,7 @@ remove_bucket_helper = \buckets, bucket_index ->
(buckets, bucket_index) (buckets, bucket_index)
increase_size : Dict k v -> Dict k v increase_size : Dict k v -> Dict k v
increase_size = \@Dict({ data, max_bucket_capacity, max_load_factor, shifts }) -> increase_size = |@Dict({ data, max_bucket_capacity, max_load_factor, shifts })|
if max_bucket_capacity != max_bucket_count then if max_bucket_capacity != max_bucket_count then
new_shifts = shifts |> Num.sub_wrap(1) new_shifts = shifts |> Num.sub_wrap(1)
(buckets0, new_max_bucket_capacity) = alloc_buckets_from_shift(new_shifts, max_load_factor) (buckets0, new_max_bucket_capacity) = alloc_buckets_from_shift(new_shifts, max_load_factor)
@ -890,7 +890,7 @@ increase_size = \@Dict({ data, max_bucket_capacity, max_load_factor, shifts }) -
crash("Dict hit limit of ${Num.to_str(max_bucket_count)} elements. Unable to grow more.") crash("Dict hit limit of ${Num.to_str(max_bucket_count)} elements. Unable to grow more.")
alloc_buckets_from_shift : U8, F32 -> (List Bucket, U64) alloc_buckets_from_shift : U8, F32 -> (List Bucket, U64)
alloc_buckets_from_shift = \shifts, max_load_factor -> alloc_buckets_from_shift = |shifts, max_load_factor|
bucket_count = calc_num_buckets(shifts) bucket_count = calc_num_buckets(shifts)
if bucket_count == max_bucket_count then if bucket_count == max_bucket_count then
# reached the maximum, make sure we can use each bucket # reached the maximum, make sure we can use each bucket
@ -905,10 +905,10 @@ alloc_buckets_from_shift = \shifts, max_load_factor ->
(List.repeat(empty_bucket, bucket_count), max_bucket_capacity) (List.repeat(empty_bucket, bucket_count), max_bucket_capacity)
calc_shifts_for_size : U64, F32 -> U8 calc_shifts_for_size : U64, F32 -> U8
calc_shifts_for_size = \size, max_load_factor -> calc_shifts_for_size = |size, max_load_factor|
calc_shifts_for_size_helper(initial_shifts, size, max_load_factor) calc_shifts_for_size_helper(initial_shifts, size, max_load_factor)
calc_shifts_for_size_helper = \shifts, size, max_load_factor -> calc_shifts_for_size_helper = |shifts, size, max_load_factor|
max_bucket_capacity = max_bucket_capacity =
shifts shifts
|> calc_num_buckets |> calc_num_buckets
@ -920,34 +920,34 @@ calc_shifts_for_size_helper = \shifts, size, max_load_factor ->
else else
shifts shifts
calc_num_buckets = \shifts -> calc_num_buckets = |shifts|
Num.min(Num.shift_left_by(1, Num.sub_wrap(64, shifts)), max_bucket_count) Num.min(Num.shift_left_by(1, Num.sub_wrap(64, shifts)), max_bucket_count)
fill_buckets_from_data = \buckets0, data, shifts -> fill_buckets_from_data = |buckets0, data, shifts|
List.walk_with_index( List.walk_with_index(
data, data,
buckets0, buckets0,
\buckets1, (key, _), data_index -> |buckets1, (key, _), data_index|
(bucket_index, dist_and_fingerprint) = next_while_less(buckets1, key, shifts) (bucket_index, dist_and_fingerprint) = next_while_less(buckets1, key, shifts)
place_and_shift_up(buckets1, { dist_and_fingerprint, data_index: Num.to_u32(data_index) }, bucket_index), place_and_shift_up(buckets1, { dist_and_fingerprint, data_index: Num.to_u32(data_index) }, bucket_index),
) )
next_while_less : List Bucket, k, U8 -> (U64, U32) where k implements Hash & Eq next_while_less : List Bucket, k, U8 -> (U64, U32) where k implements Hash & Eq
next_while_less = \buckets, key, shifts -> next_while_less = |buckets, key, shifts|
hash = hash_key(key) hash = hash_key(key)
dist_and_fingerprint = dist_and_fingerprint_from_hash(hash) dist_and_fingerprint = dist_and_fingerprint_from_hash(hash)
bucket_index = bucket_index_from_hash(hash, shifts) bucket_index = bucket_index_from_hash(hash, shifts)
next_while_less_helper(buckets, bucket_index, dist_and_fingerprint) next_while_less_helper(buckets, bucket_index, dist_and_fingerprint)
next_while_less_helper = \buckets, bucket_index, dist_and_fingerprint -> next_while_less_helper = |buckets, bucket_index, dist_and_fingerprint|
loaded = list_get_unsafe(buckets, bucket_index) loaded = list_get_unsafe(buckets, bucket_index)
if dist_and_fingerprint < loaded.dist_and_fingerprint then if dist_and_fingerprint < loaded.dist_and_fingerprint then
next_while_less_helper(buckets, next_bucket_index(bucket_index, List.len(buckets)), increment_dist(dist_and_fingerprint)) next_while_less_helper(buckets, next_bucket_index(bucket_index, List.len(buckets)), increment_dist(dist_and_fingerprint))
else else
(bucket_index, dist_and_fingerprint) (bucket_index, dist_and_fingerprint)
place_and_shift_up = \buckets0, bucket, bucket_index -> place_and_shift_up = |buckets0, bucket, bucket_index|
loaded = list_get_unsafe(buckets0, bucket_index) loaded = list_get_unsafe(buckets0, bucket_index)
if loaded.dist_and_fingerprint != 0 then if loaded.dist_and_fingerprint != 0 then
buckets1 = List.set(buckets0, bucket_index, bucket) buckets1 = List.set(buckets0, bucket_index, bucket)
@ -959,7 +959,7 @@ place_and_shift_up = \buckets0, bucket, bucket_index ->
else else
List.set(buckets0, bucket_index, bucket) List.set(buckets0, bucket_index, bucket)
next_bucket_index = \bucket_index, max_buckets -> next_bucket_index = |bucket_index, max_buckets|
# I just ported this impl directly. # I just ported this impl directly.
# I am a bit confused why it is using an if over a mask. # I am a bit confused why it is using an if over a mask.
# Maybe compilers are smart enough to optimize this well. # Maybe compilers are smart enough to optimize this well.
@ -969,20 +969,20 @@ next_bucket_index = \bucket_index, max_buckets ->
else else
0 0
hash_key = \key -> hash_key = |key|
create_low_level_hasher(PseudoRandSeed) create_low_level_hasher(PseudoRandSeed)
|> Hash.hash(key) |> Hash.hash(key)
|> complete |> complete
dist_and_fingerprint_from_hash : U64 -> U32 dist_and_fingerprint_from_hash : U64 -> U32
dist_and_fingerprint_from_hash = \hash -> dist_and_fingerprint_from_hash = |hash|
hash hash
|> Num.to_u32 |> Num.to_u32
|> Num.bitwise_and(fingerprint_mask) |> Num.bitwise_and(fingerprint_mask)
|> Num.bitwise_or(dist_inc) |> Num.bitwise_or(dist_inc)
bucket_index_from_hash : U64, U8 -> U64 bucket_index_from_hash : U64, U8 -> U64
bucket_index_from_hash = \hash, shifts -> bucket_index_from_hash = |hash, shifts|
Num.shift_right_zf_by(hash, shifts) Num.shift_right_zf_by(hash, shifts)
expect expect
@ -1227,7 +1227,7 @@ BadKey := U64 implements [
] ]
hash_bad_key : hasher, BadKey -> hasher where hasher implements Hasher hash_bad_key : hasher, BadKey -> hasher where hasher implements Hasher
hash_bad_key = \hasher, _ -> Hash.hash(hasher, 0) hash_bad_key = |hasher, _| Hash.hash(hasher, 0)
expect expect
bad_keys = [ bad_keys = [
@ -1250,11 +1250,11 @@ expect
List.walk( List.walk(
bad_keys, bad_keys,
Dict.empty({}), Dict.empty({}),
\acc, k -> |acc, k|
Dict.update( Dict.update(
acc, acc,
k, k,
\val -> |val|
when val is when val is
Ok(p) -> Ok(Num.add_wrap(p, 1)) Ok(p) -> Ok(Num.add_wrap(p, 1))
Err(Missing) -> Ok(0), Err(Missing) -> Ok(0),
@ -1265,7 +1265,7 @@ expect
List.walk( List.walk(
bad_keys, bad_keys,
Bool.true, Bool.true,
\acc, k -> |acc, k|
acc && Dict.contains(dict, k), acc && Dict.contains(dict, k),
) )
@ -1299,7 +1299,7 @@ LowLevelHasher := { initialized_seed : U64, state : U64 } implements [
pseudo_seed : {} -> U64 pseudo_seed : {} -> U64
create_low_level_hasher : [PseudoRandSeed, WithSeed U64] -> LowLevelHasher create_low_level_hasher : [PseudoRandSeed, WithSeed U64] -> LowLevelHasher
create_low_level_hasher = \seed_opt -> create_low_level_hasher = |seed_opt|
seed = seed =
when seed_opt is when seed_opt is
PseudoRandSeed -> pseudo_seed({}) PseudoRandSeed -> pseudo_seed({})
@ -1307,7 +1307,7 @@ create_low_level_hasher = \seed_opt ->
@LowLevelHasher({ initialized_seed: init_seed(seed), state: seed }) @LowLevelHasher({ initialized_seed: init_seed(seed), state: seed })
combine_state : LowLevelHasher, { a : U64, b : U64, seed : U64, length : U64 } -> LowLevelHasher combine_state : LowLevelHasher, { a : U64, b : U64, seed : U64, length : U64 } -> LowLevelHasher
combine_state = \@LowLevelHasher({ initialized_seed, state }), { a, b, seed, length } -> combine_state = |@LowLevelHasher({ initialized_seed, state }), { a, b, seed, length }|
mum = mum =
a a
|> Num.bitwise_xor(wyp1) |> Num.bitwise_xor(wyp1)
@ -1323,20 +1323,20 @@ combine_state = \@LowLevelHasher({ initialized_seed, state }), { a, b, seed, len
@LowLevelHasher({ initialized_seed, state: wymix(state, hash) }) @LowLevelHasher({ initialized_seed, state: wymix(state, hash) })
init_seed = \seed -> init_seed = |seed|
seed seed
|> Num.bitwise_xor(wyp0) |> Num.bitwise_xor(wyp0)
|> wymix(wyp1) |> wymix(wyp1)
|> Num.bitwise_xor(seed) |> Num.bitwise_xor(seed)
complete = \@LowLevelHasher({ state }) -> state complete = |@LowLevelHasher({ state })| state
# These implementations hash each value individually with the seed and then mix # These implementations hash each value individually with the seed and then mix
# the resulting hash with the state. There are other options that may be faster # the resulting hash with the state. There are other options that may be faster
# like using the output of the last hash as the seed to the current hash. # like using the output of the last hash as the seed to the current hash.
# I am simply not sure the tradeoffs here. Theoretically this method is more sound. # I am simply not sure the tradeoffs here. Theoretically this method is more sound.
# Either way, the performance will be similar and we can change this later. # Either way, the performance will be similar and we can change this later.
add_u8 = \@LowLevelHasher({ initialized_seed, state }), u8 -> add_u8 = |@LowLevelHasher({ initialized_seed, state }), u8|
p0 = Num.to_u64(u8) p0 = Num.to_u64(u8)
a = a =
Num.shift_left_by(p0, 16) Num.shift_left_by(p0, 16)
@ -1346,7 +1346,7 @@ add_u8 = \@LowLevelHasher({ initialized_seed, state }), u8 ->
combine_state(@LowLevelHasher({ initialized_seed, state }), { a, b, seed: initialized_seed, length: 1 }) combine_state(@LowLevelHasher({ initialized_seed, state }), { a, b, seed: initialized_seed, length: 1 })
add_u16 = \@LowLevelHasher({ initialized_seed, state }), u16 -> add_u16 = |@LowLevelHasher({ initialized_seed, state }), u16|
p0 = Num.bitwise_and(u16, 0xFF) |> Num.to_u64 p0 = Num.bitwise_and(u16, 0xFF) |> Num.to_u64
p1 = Num.shift_right_zf_by(u16, 8) |> Num.to_u64 p1 = Num.shift_right_zf_by(u16, 8) |> Num.to_u64
a = a =
@ -1357,13 +1357,13 @@ add_u16 = \@LowLevelHasher({ initialized_seed, state }), u16 ->
combine_state(@LowLevelHasher({ initialized_seed, state }), { a, b, seed: initialized_seed, length: 2 }) combine_state(@LowLevelHasher({ initialized_seed, state }), { a, b, seed: initialized_seed, length: 2 })
add_u32 = \@LowLevelHasher({ initialized_seed, state }), u32 -> add_u32 = |@LowLevelHasher({ initialized_seed, state }), u32|
p0 = Num.to_u64(u32) p0 = Num.to_u64(u32)
a = Num.shift_left_by(p0, 32) |> Num.bitwise_or(p0) a = Num.shift_left_by(p0, 32) |> Num.bitwise_or(p0)
combine_state(@LowLevelHasher({ initialized_seed, state }), { a, b: a, seed: initialized_seed, length: 4 }) combine_state(@LowLevelHasher({ initialized_seed, state }), { a, b: a, seed: initialized_seed, length: 4 })
add_u64 = \@LowLevelHasher({ initialized_seed, state }), u64 -> add_u64 = |@LowLevelHasher({ initialized_seed, state }), u64|
p0 = Num.bitwise_and(0xFFFF_FFFF, u64) p0 = Num.bitwise_and(0xFFFF_FFFF, u64)
p1 = Num.shift_right_zf_by(u64, 32) p1 = Num.shift_right_zf_by(u64, 32)
a = Num.shift_left_by(p0, 32) |> Num.bitwise_or(p1) a = Num.shift_left_by(p0, 32) |> Num.bitwise_or(p1)
@ -1371,7 +1371,7 @@ add_u64 = \@LowLevelHasher({ initialized_seed, state }), u64 ->
combine_state(@LowLevelHasher({ initialized_seed, state }), { a, b, seed: initialized_seed, length: 8 }) combine_state(@LowLevelHasher({ initialized_seed, state }), { a, b, seed: initialized_seed, length: 8 })
add_u128 = \@LowLevelHasher({ initialized_seed, state }), u128 -> add_u128 = |@LowLevelHasher({ initialized_seed, state }), u128|
lower = u128 |> Num.to_u64 lower = u128 |> Num.to_u64
upper = Num.shift_right_zf_by(u128, 64) |> Num.to_u64 upper = Num.shift_right_zf_by(u128, 64) |> Num.to_u64
p0 = Num.bitwise_and(0xFFFF_FFFF, lower) p0 = Num.bitwise_and(0xFFFF_FFFF, lower)
@ -1384,7 +1384,7 @@ add_u128 = \@LowLevelHasher({ initialized_seed, state }), u128 ->
combine_state(@LowLevelHasher({ initialized_seed, state }), { a, b, seed: initialized_seed, length: 16 }) combine_state(@LowLevelHasher({ initialized_seed, state }), { a, b, seed: initialized_seed, length: 16 })
add_bytes : LowLevelHasher, List U8 -> LowLevelHasher add_bytes : LowLevelHasher, List U8 -> LowLevelHasher
add_bytes = \@LowLevelHasher({ initialized_seed, state }), list -> add_bytes = |@LowLevelHasher({ initialized_seed, state }), list|
length = List.len(list) length = List.len(list)
abs = abs =
if length <= 16 then if length <= 16 then
@ -1409,7 +1409,7 @@ add_bytes = \@LowLevelHasher({ initialized_seed, state }), list ->
combine_state(@LowLevelHasher({ initialized_seed, state }), { a: abs.a, b: abs.b, seed: abs.seed, length }) combine_state(@LowLevelHasher({ initialized_seed, state }), { a: abs.a, b: abs.b, seed: abs.seed, length })
hash_bytes_helper48 : U64, U64, U64, List U8, U64, U64 -> { a : U64, b : U64, seed : U64 } hash_bytes_helper48 : U64, U64, U64, List U8, U64, U64 -> { a : U64, b : U64, seed : U64 }
hash_bytes_helper48 = \seed, see1, see2, list, index, remaining -> hash_bytes_helper48 = |seed, see1, see2, list, index, remaining|
new_seed = wymix(Num.bitwise_xor(wyr8(list, index), wyp1), Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 8)), seed)) new_seed = wymix(Num.bitwise_xor(wyr8(list, index), wyp1), Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 8)), seed))
new_see1 = wymix(Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 16)), wyp2), Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 24)), see1)) new_see1 = wymix(Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 16)), wyp2), Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 24)), see1))
new_see2 = wymix(Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 32)), wyp3), Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 40)), see2)) new_see2 = wymix(Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 32)), wyp3), Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 40)), see2))
@ -1428,7 +1428,7 @@ hash_bytes_helper48 = \seed, see1, see2, list, index, remaining ->
{ a: wyr8(list, (Num.sub_wrap(new_remaining, 16) |> Num.add_wrap(new_index))), b: wyr8(list, (Num.sub_wrap(new_remaining, 8) |> Num.add_wrap(new_index))), seed: final_seed } { a: wyr8(list, (Num.sub_wrap(new_remaining, 16) |> Num.add_wrap(new_index))), b: wyr8(list, (Num.sub_wrap(new_remaining, 8) |> Num.add_wrap(new_index))), seed: final_seed }
hash_bytes_helper16 : U64, List U8, U64, U64 -> { a : U64, b : U64, seed : U64 } hash_bytes_helper16 : U64, List U8, U64, U64 -> { a : U64, b : U64, seed : U64 }
hash_bytes_helper16 = \seed, list, index, remaining -> hash_bytes_helper16 = |seed, list, index, remaining|
new_seed = wymix(Num.bitwise_xor(wyr8(list, index), wyp1), Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 8)), seed)) new_seed = wymix(Num.bitwise_xor(wyr8(list, index), wyp1), Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 8)), seed))
new_remaining = Num.sub_wrap(remaining, 16) new_remaining = Num.sub_wrap(remaining, 16)
new_index = Num.add_wrap(index, 16) new_index = Num.add_wrap(index, 16)
@ -1448,13 +1448,13 @@ wyp3 : U64
wyp3 = 0x589965cc75374cc3 wyp3 = 0x589965cc75374cc3
wymix : U64, U64 -> U64 wymix : U64, U64 -> U64
wymix = \a, b -> wymix = |a, b|
{ lower, upper } = wymum(a, b) { lower, upper } = wymum(a, b)
Num.bitwise_xor(lower, upper) Num.bitwise_xor(lower, upper)
wymum : U64, U64 -> { lower : U64, upper : U64 } wymum : U64, U64 -> { lower : U64, upper : U64 }
wymum = \a, b -> wymum = |a, b|
r = Num.mul_wrap(Num.to_u128(a), Num.to_u128(b)) r = Num.mul_wrap(Num.to_u128(a), Num.to_u128(b))
lower = Num.to_u64(r) lower = Num.to_u64(r)
upper = Num.shift_right_zf_by(r, 64) |> Num.to_u64 upper = Num.shift_right_zf_by(r, 64) |> Num.to_u64
@ -1465,7 +1465,7 @@ wymum = \a, b ->
# Get the next 8 bytes as a U64 # Get the next 8 bytes as a U64
wyr8 : List U8, U64 -> U64 wyr8 : List U8, U64 -> U64
wyr8 = \list, index -> wyr8 = |list, index|
# With seamless slices and Num.from_bytes, this should be possible to make faster and nicer. # With seamless slices and Num.from_bytes, this should be possible to make faster and nicer.
# It would also deal with the fact that on big endian systems we want to invert the order here. # It would also deal with the fact that on big endian systems we want to invert the order here.
# Without seamless slices, we would need from_bytes to take an index. # Without seamless slices, we would need from_bytes to take an index.
@ -1486,7 +1486,7 @@ wyr8 = \list, index ->
# Get the next 4 bytes as a U64 with some shifting. # Get the next 4 bytes as a U64 with some shifting.
wyr4 : List U8, U64 -> U64 wyr4 : List U8, U64 -> U64
wyr4 = \list, index -> wyr4 = |list, index|
p1 = list_get_unsafe(list, index) |> Num.to_u64 p1 = list_get_unsafe(list, index) |> Num.to_u64
p2 = list_get_unsafe(list, Num.add_wrap(index, 1)) |> Num.to_u64 p2 = list_get_unsafe(list, Num.add_wrap(index, 1)) |> Num.to_u64
p3 = list_get_unsafe(list, Num.add_wrap(index, 2)) |> Num.to_u64 p3 = list_get_unsafe(list, Num.add_wrap(index, 2)) |> Num.to_u64
@ -1499,7 +1499,7 @@ wyr4 = \list, index ->
# Get the next K bytes with some shifting. # Get the next K bytes with some shifting.
# K must be 3 or less. # K must be 3 or less.
wyr3 : List U8, U64, U64 -> U64 wyr3 : List U8, U64, U64 -> U64
wyr3 = \list, index, k -> wyr3 = |list, index, k|
# ((uint64_t)p[0])<<16)|(((uint64_t)p[k>>1])<<8)|p[k-1] # ((uint64_t)p[0])<<16)|(((uint64_t)p[k>>1])<<8)|p[k-1]
p1 = list_get_unsafe(list, index) |> Num.to_u64 p1 = list_get_unsafe(list, index) |> Num.to_u64
p2 = list_get_unsafe(list, Num.shift_right_zf_by(k, 1) |> Num.add_wrap(index)) |> Num.to_u64 p2 = list_get_unsafe(list, Num.shift_right_zf_by(k, 1) |> Num.add_wrap(index)) |> Num.to_u64
@ -1704,7 +1704,7 @@ expect
|> Dict.insert("Alice", 17) |> Dict.insert("Alice", 17)
|> Dict.insert("Bob", 18) |> Dict.insert("Bob", 18)
|> Dict.insert("Charlie", 19) |> Dict.insert("Charlie", 19)
|> Dict.walk_until(Bool.false, \_, _, age -> if age >= 18 then Break(Bool.true) else Continue(Bool.false)) |> Dict.walk_until(Bool.false, |_, _, age| if age >= 18 then Break(Bool.true) else Continue(Bool.false))
|> Bool.is_eq(Bool.true) |> Bool.is_eq(Bool.true)
expect expect
@ -1713,7 +1713,7 @@ expect
|> Dict.insert("Alice", 17) |> Dict.insert("Alice", 17)
|> Dict.insert("Bob", 18) |> Dict.insert("Bob", 18)
|> Dict.insert("Charlie", 19) |> Dict.insert("Charlie", 19)
|> Dict.keep_if(\(_k, v) -> v >= 18) |> Dict.keep_if(|(_k, v)| v >= 18)
d2 = d2 =
Dict.empty({}) Dict.empty({})
@ -1728,7 +1728,7 @@ expect
|> Dict.insert("Alice", 17) |> Dict.insert("Alice", 17)
|> Dict.insert("Bob", 18) |> Dict.insert("Bob", 18)
|> Dict.insert("Charlie", 19) |> Dict.insert("Charlie", 19)
|> Dict.keep_if(\(k, _v) -> Str.ends_with(k, "e")) |> Dict.keep_if(|(k, _v)| Str.ends_with(k, "e"))
d2 = d2 =
Dict.empty({}) Dict.empty({})
@ -1746,7 +1746,7 @@ expect
|> Dict.insert(2, 2) |> Dict.insert(2, 2)
|> Dict.insert(3, 3) |> Dict.insert(3, 3)
|> Dict.insert(4, 4) |> Dict.insert(4, 4)
|> Dict.keep_if(\(k, _v) -> !(List.contains(keys_to_delete, k))) |> Dict.keep_if(|(k, _v)| !(List.contains(keys_to_delete, k)))
d2 = d2 =
Dict.empty({}) Dict.empty({})
@ -1765,7 +1765,7 @@ expect
|> Dict.insert(2, 2) |> Dict.insert(2, 2)
|> Dict.insert(3, 3) |> Dict.insert(3, 3)
|> Dict.insert(4, 4) |> Dict.insert(4, 4)
|> Dict.keep_if(\(k, _v) -> !(List.contains(keys_to_delete, k))) |> Dict.keep_if(|(k, _v)| !(List.contains(keys_to_delete, k)))
d2 = d2 =
Dict.empty({}) Dict.empty({})

View file

@ -84,10 +84,10 @@ EncoderFormatting implements
## actual == expected ## actual == expected
## ``` ## ```
custom : (List U8, fmt -> List U8) -> Encoder fmt where fmt implements EncoderFormatting custom : (List U8, fmt -> List U8) -> Encoder fmt where fmt implements EncoderFormatting
custom = \encoder -> @Encoder(encoder) custom = |encoder| @Encoder(encoder)
append_with : List U8, Encoder fmt, fmt -> List U8 where fmt implements EncoderFormatting append_with : List U8, Encoder fmt, fmt -> List U8 where fmt implements EncoderFormatting
append_with = \lst, @Encoder(do_encoding), fmt -> do_encoding(lst, fmt) append_with = |lst, @Encoder(do_encoding), fmt| do_encoding(lst, fmt)
## Appends the encoded representation of a value to an existing list of bytes. ## Appends the encoded representation of a value to an existing list of bytes.
## ##
@ -99,7 +99,7 @@ append_with = \lst, @Encoder(do_encoding), fmt -> do_encoding(lst, fmt)
## actual == expected ## actual == expected
## ``` ## ```
append : List U8, val, fmt -> List U8 where val implements Encoding, fmt implements EncoderFormatting append : List U8, val, fmt -> List U8 where val implements Encoding, fmt implements EncoderFormatting
append = \lst, val, fmt -> append_with(lst, to_encoder(val), fmt) append = |lst, val, fmt| append_with(lst, to_encoder(val), fmt)
## Encodes a value to a list of bytes (`List U8`) according to the specified format. ## Encodes a value to a list of bytes (`List U8`) according to the specified format.
## ##
@ -113,4 +113,4 @@ append = \lst, val, fmt -> append_with(lst, to_encoder(val), fmt)
## actual == expected ## actual == expected
## ``` ## ```
to_bytes : val, fmt -> List U8 where val implements Encoding, fmt implements EncoderFormatting to_bytes : val, fmt -> List U8 where val implements Encoding, fmt implements EncoderFormatting
to_bytes = \val, fmt -> append_with([], to_encoder(val), fmt) to_bytes = |val, fmt| append_with([], to_encoder(val), fmt)

View file

@ -74,56 +74,56 @@ Hasher implements
complete : a -> U64 where a implements Hasher complete : a -> U64 where a implements Hasher
## Adds a string into a [Hasher] by hashing its UTF-8 bytes. ## Adds a string into a [Hasher] by hashing its UTF-8 bytes.
hash_str_bytes = \hasher, s -> hash_str_bytes = |hasher, s|
add_bytes(hasher, Str.to_utf8(s)) add_bytes(hasher, Str.to_utf8(s))
## Adds a list of [Hash]able elements to a [Hasher] by hashing each element. ## Adds a list of [Hash]able elements to a [Hasher] by hashing each element.
hash_list = \hasher, lst -> hash_list = |hasher, lst|
List.walk( List.walk(
lst, lst,
hasher, hasher,
\accum_hasher, elem -> |accum_hasher, elem|
hash(accum_hasher, elem), hash(accum_hasher, elem),
) )
## Adds a single [Bool] to a hasher. ## Adds a single [Bool] to a hasher.
hash_bool : a, Bool -> a where a implements Hasher hash_bool : a, Bool -> a where a implements Hasher
hash_bool = \hasher, b -> hash_bool = |hasher, b|
as_u8 = if b then 1 else 0 as_u8 = if b then 1 else 0
add_u8(hasher, as_u8) add_u8(hasher, as_u8)
## Adds a single I8 to a hasher. ## Adds a single I8 to a hasher.
hash_i8 : a, I8 -> a where a implements Hasher hash_i8 : a, I8 -> a where a implements Hasher
hash_i8 = \hasher, n -> add_u8(hasher, Num.to_u8(n)) hash_i8 = |hasher, n| add_u8(hasher, Num.to_u8(n))
## Adds a single I16 to a hasher. ## Adds a single I16 to a hasher.
hash_i16 : a, I16 -> a where a implements Hasher hash_i16 : a, I16 -> a where a implements Hasher
hash_i16 = \hasher, n -> add_u16(hasher, Num.to_u16(n)) hash_i16 = |hasher, n| add_u16(hasher, Num.to_u16(n))
## Adds a single I32 to a hasher. ## Adds a single I32 to a hasher.
hash_i32 : a, I32 -> a where a implements Hasher hash_i32 : a, I32 -> a where a implements Hasher
hash_i32 = \hasher, n -> add_u32(hasher, Num.to_u32(n)) hash_i32 = |hasher, n| add_u32(hasher, Num.to_u32(n))
## Adds a single I64 to a hasher. ## Adds a single I64 to a hasher.
hash_i64 : a, I64 -> a where a implements Hasher hash_i64 : a, I64 -> a where a implements Hasher
hash_i64 = \hasher, n -> add_u64(hasher, Num.to_u64(n)) hash_i64 = |hasher, n| add_u64(hasher, Num.to_u64(n))
## Adds a single I128 to a hasher. ## Adds a single I128 to a hasher.
hash_i128 : a, I128 -> a where a implements Hasher hash_i128 : a, I128 -> a where a implements Hasher
hash_i128 = \hasher, n -> add_u128(hasher, Num.to_u128(n)) hash_i128 = |hasher, n| add_u128(hasher, Num.to_u128(n))
## Adds a single [Dec] to a hasher. ## Adds a single [Dec] to a hasher.
hash_dec : a, Dec -> a where a implements Hasher hash_dec : a, Dec -> a where a implements Hasher
hash_dec = \hasher, n -> hash_i128(hasher, Num.without_decimal_point(n)) hash_dec = |hasher, n| hash_i128(hasher, Num.without_decimal_point(n))
## Adds a container of [Hash]able elements to a [Hasher] by hashing each element. ## 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 container is iterated using the walk method passed in.
## The order of the elements does not affect the final hash. ## The order of the elements does not affect the final hash.
hash_unordered = \hasher, container, walk -> hash_unordered = |hasher, container, walk|
walk( walk(
container, container,
0, 0,
\accum, elem -> |accum, elem|
x = x =
# Note, we intentionally copy the hasher in every iteration. # Note, we intentionally copy the hasher in every iteration.
# Having the same base state is required for unordered hashing. # Having the same base state is required for unordered hashing.
@ -138,4 +138,4 @@ hash_unordered = \hasher, container, walk ->
else else
next_accum, next_accum,
) )
|> \accum -> add_u64(hasher, accum) |> |accum| add_u64(hasher, accum)

View file

@ -81,21 +81,21 @@ InspectFormatter implements
Inspector f := f -> f where f implements InspectFormatter Inspector f := f -> f where f implements InspectFormatter
custom : (f -> f) -> Inspector f where f implements InspectFormatter custom : (f -> f) -> Inspector f where f implements InspectFormatter
custom = \fn -> @Inspector(fn) custom = |fn| @Inspector(fn)
apply : Inspector f, f -> f where f implements InspectFormatter apply : Inspector f, f -> f where f implements InspectFormatter
apply = \@Inspector(fn), fmt -> fn(fmt) apply = |@Inspector(fn), fmt| fn(fmt)
Inspect implements Inspect implements
to_inspector : val -> Inspector f where val implements Inspect, f implements InspectFormatter to_inspector : val -> Inspector f where val implements Inspect, f implements InspectFormatter
inspect : val -> f where val implements Inspect, f implements InspectFormatter inspect : val -> f where val implements Inspect, f implements InspectFormatter
inspect = \val -> inspect = |val|
@Inspector(val_fn) = to_inspector(val) @Inspector(val_fn) = to_inspector(val)
val_fn(init({})) val_fn(init({}))
to_str : val -> Str where val implements Inspect to_str : val -> Str where val implements Inspect
to_str = \val -> to_str = |val|
val val
|> inspect |> inspect
|> to_dbg_str |> to_dbg_str
@ -134,16 +134,16 @@ DbgFormatter := { data : Str }
] ]
dbg_init : {} -> DbgFormatter dbg_init : {} -> DbgFormatter
dbg_init = \{} -> @DbgFormatter({ data: "" }) dbg_init = |{}| @DbgFormatter({ data: "" })
dbg_list : list, ElemWalker (DbgFormatter, Bool) list elem, (elem -> Inspector DbgFormatter) -> Inspector DbgFormatter dbg_list : list, ElemWalker (DbgFormatter, Bool) list elem, (elem -> Inspector DbgFormatter) -> Inspector DbgFormatter
dbg_list = \content, walk_fn, to_dbg_inspector -> dbg_list = |content, walk_fn, to_dbg_inspector|
custom_list_dbg = \f0 -> custom_list_dbg = |f0|
f1 = dbg_write(f0, "[") f1 = dbg_write(f0, "[")
(f5, _) = walk_fn( (f5, _) = walk_fn(
content, content,
(f1, Bool.false), (f1, Bool.false),
\(f2, prepend_sep), elem -> |(f2, prepend_sep), elem|
f3 = f3 =
if prepend_sep then if prepend_sep then
dbg_write(f2, ", ") dbg_write(f2, ", ")
@ -153,7 +153,7 @@ dbg_list = \content, walk_fn, to_dbg_inspector ->
elem elem
|> to_dbg_inspector |> to_dbg_inspector
|> apply(f3) |> apply(f3)
|> \f4 -> (f4, Bool.true), |> |f4| (f4, Bool.true),
) )
dbg_write(f5, "]") dbg_write(f5, "]")
@ -161,13 +161,13 @@ dbg_list = \content, walk_fn, to_dbg_inspector ->
custom(custom_list_dbg) custom(custom_list_dbg)
dbg_set : set, ElemWalker (DbgFormatter, Bool) set elem, (elem -> Inspector DbgFormatter) -> Inspector DbgFormatter dbg_set : set, ElemWalker (DbgFormatter, Bool) set elem, (elem -> Inspector DbgFormatter) -> Inspector DbgFormatter
dbg_set = \content, walk_fn, to_dbg_inspector -> dbg_set = |content, walk_fn, to_dbg_inspector|
custom_dbg_set = \f0 -> custom_dbg_set = |f0|
f1 = dbg_write(f0, "{") f1 = dbg_write(f0, "{")
(f5, _) = walk_fn( (f5, _) = walk_fn(
content, content,
(f1, Bool.false), (f1, Bool.false),
\(f2, prepend_sep), elem -> |(f2, prepend_sep), elem|
f3 = f3 =
if prepend_sep then if prepend_sep then
dbg_write(f2, ", ") dbg_write(f2, ", ")
@ -177,7 +177,7 @@ dbg_set = \content, walk_fn, to_dbg_inspector ->
elem elem
|> to_dbg_inspector |> to_dbg_inspector
|> apply(f3) |> apply(f3)
|> \f4 -> (f4, Bool.true), |> |f4| (f4, Bool.true),
) )
dbg_write(f5, "}") dbg_write(f5, "}")
@ -185,13 +185,13 @@ dbg_set = \content, walk_fn, to_dbg_inspector ->
custom(custom_dbg_set) custom(custom_dbg_set)
dbg_dict : dict, KeyValWalker (DbgFormatter, Bool) dict key value, (key -> Inspector DbgFormatter), (value -> Inspector DbgFormatter) -> Inspector DbgFormatter dbg_dict : dict, KeyValWalker (DbgFormatter, Bool) dict key value, (key -> Inspector DbgFormatter), (value -> Inspector DbgFormatter) -> Inspector DbgFormatter
dbg_dict = \d, walk_fn, key_to_inspector, value_to_inspector -> dbg_dict = |d, walk_fn, key_to_inspector, value_to_inspector|
custom_dbg_dict = \f0 -> custom_dbg_dict = |f0|
f1 = dbg_write(f0, "{") f1 = dbg_write(f0, "{")
(f5, _) = walk_fn( (f5, _) = walk_fn(
d, d,
(f1, Bool.false), (f1, Bool.false),
\(f2, prepend_sep), key, value -> |(f2, prepend_sep), key, value|
f3 = f3 =
if prepend_sep then if prepend_sep then
dbg_write(f2, ", ") dbg_write(f2, ", ")
@ -200,8 +200,8 @@ dbg_dict = \d, walk_fn, key_to_inspector, value_to_inspector ->
apply(key_to_inspector(key), f3) apply(key_to_inspector(key), f3)
|> dbg_write(": ") |> dbg_write(": ")
|> \x -> apply(value_to_inspector(value), x) |> |x| apply(value_to_inspector(value), x)
|> \f4 -> (f4, Bool.true), |> |f4| (f4, Bool.true),
) )
dbg_write(f5, "}") dbg_write(f5, "}")
@ -209,11 +209,11 @@ dbg_dict = \d, walk_fn, key_to_inspector, value_to_inspector ->
custom(custom_dbg_dict) custom(custom_dbg_dict)
dbg_tag : Str, List (Inspector DbgFormatter) -> Inspector DbgFormatter dbg_tag : Str, List (Inspector DbgFormatter) -> Inspector DbgFormatter
dbg_tag = \name, fields -> dbg_tag = |name, fields|
if List.is_empty(fields) then if List.is_empty(fields) then
custom(\f0 -> dbg_write(f0, name)) custom(|f0| dbg_write(f0, name))
else else
custom_dbg_tag = \f0 -> custom_dbg_tag = |f0|
f1 = f1 =
dbg_write(f0, "(") dbg_write(f0, "(")
|> dbg_write(name) |> dbg_write(name)
@ -221,9 +221,9 @@ dbg_tag = \name, fields ->
f3 = List.walk( f3 = List.walk(
fields, fields,
f1, f1,
\f2, inspector -> |f2, inspector|
dbg_write(f2, " ") dbg_write(f2, " ")
|> \x -> apply(inspector, x), |> |x| apply(inspector, x),
) )
dbg_write(f3, ")") dbg_write(f3, ")")
@ -231,13 +231,13 @@ dbg_tag = \name, fields ->
custom(custom_dbg_tag) custom(custom_dbg_tag)
dbg_tuple : List (Inspector DbgFormatter) -> Inspector DbgFormatter dbg_tuple : List (Inspector DbgFormatter) -> Inspector DbgFormatter
dbg_tuple = \fields -> dbg_tuple = |fields|
custom_dbg_tuple = \f0 -> custom_dbg_tuple = |f0|
f1 = dbg_write(f0, "(") f1 = dbg_write(f0, "(")
(f5, _) = List.walk( (f5, _) = List.walk(
fields, fields,
(f1, Bool.false), (f1, Bool.false),
\(f2, prepend_sep), inspector -> |(f2, prepend_sep), inspector|
f3 = f3 =
if prepend_sep then if prepend_sep then
dbg_write(f2, ", ") dbg_write(f2, ", ")
@ -245,7 +245,7 @@ dbg_tuple = \fields ->
f2 f2
apply(inspector, f3) apply(inspector, f3)
|> \f4 -> (f4, Bool.true), |> |f4| (f4, Bool.true),
) )
dbg_write(f5, ")") dbg_write(f5, ")")
@ -253,13 +253,13 @@ dbg_tuple = \fields ->
custom(custom_dbg_tuple) custom(custom_dbg_tuple)
dbg_record : List { key : Str, value : Inspector DbgFormatter } -> Inspector DbgFormatter dbg_record : List { key : Str, value : Inspector DbgFormatter } -> Inspector DbgFormatter
dbg_record = \fields -> dbg_record = |fields|
custom_dbg_record = \f0 -> custom_dbg_record = |f0|
f1 = dbg_write(f0, "{") f1 = dbg_write(f0, "{")
(f5, _) = List.walk( (f5, _) = List.walk(
fields, fields,
(f1, Bool.false), (f1, Bool.false),
\(f2, prepend_sep), { key, value } -> |(f2, prepend_sep), { key, value }|
f3 = f3 =
if prepend_sep then if prepend_sep then
dbg_write(f2, ", ") dbg_write(f2, ", ")
@ -268,8 +268,8 @@ dbg_record = \fields ->
dbg_write(f3, key) dbg_write(f3, key)
|> dbg_write(": ") |> dbg_write(": ")
|> \x -> apply(value, x) |> |x| apply(value, x)
|> \f4 -> (f4, Bool.true), |> |f4| (f4, Bool.true),
) )
dbg_write(f5, "}") dbg_write(f5, "}")
@ -277,12 +277,12 @@ dbg_record = \fields ->
custom(custom_dbg_record) custom(custom_dbg_record)
dbg_bool : Bool -> Inspector DbgFormatter dbg_bool : Bool -> Inspector DbgFormatter
dbg_bool = \b -> dbg_bool = |b|
text = if b then "Bool.true" else "Bool.false" text = if b then "Bool.true" else "Bool.false"
custom(\f0 -> dbg_write(f0, text)) custom(|f0| dbg_write(f0, text))
dbg_str : Str -> Inspector DbgFormatter dbg_str : Str -> Inspector DbgFormatter
dbg_str = \s -> dbg_str = |s|
# escape invisible unicode characters as in fmt_str_body crates/compiler/fmt/src/expr.rs # escape invisible unicode characters as in fmt_str_body crates/compiler/fmt/src/expr.rs
escape_s = escape_s =
Str.replace_each(s, "\u(feff)", "\\u(feff)") Str.replace_each(s, "\u(feff)", "\\u(feff)")
@ -290,7 +290,7 @@ dbg_str = \s ->
|> Str.replace_each("\u(200c)", "\\u(200c)") |> Str.replace_each("\u(200c)", "\\u(200c)")
|> Str.replace_each("\u(200d)", "\\u(200d)") |> Str.replace_each("\u(200d)", "\\u(200d)")
custom_dbg_str = \f0 -> custom_dbg_str = |f0|
dbg_write(f0, "\"") dbg_write(f0, "\"")
|> dbg_write(escape_s) |> dbg_write(escape_s)
|> dbg_write("\"") |> dbg_write("\"")
@ -298,68 +298,68 @@ dbg_str = \s ->
custom(custom_dbg_str) custom(custom_dbg_str)
dbg_opaque : * -> Inspector DbgFormatter dbg_opaque : * -> Inspector DbgFormatter
dbg_opaque = \_ -> dbg_opaque = |_|
custom(\f0 -> dbg_write(f0, "<opaque>")) custom(|f0| dbg_write(f0, "<opaque>"))
dbg_function : * -> Inspector DbgFormatter dbg_function : * -> Inspector DbgFormatter
dbg_function = \_ -> dbg_function = |_|
custom(\f0 -> dbg_write(f0, "<function>")) custom(|f0| dbg_write(f0, "<function>"))
dbg_u8 : U8 -> Inspector DbgFormatter dbg_u8 : U8 -> Inspector DbgFormatter
dbg_u8 = \num -> dbg_u8 = |num|
custom(\f0 -> dbg_write(f0, Num.to_str(num))) custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_i8 : I8 -> Inspector DbgFormatter dbg_i8 : I8 -> Inspector DbgFormatter
dbg_i8 = \num -> dbg_i8 = |num|
custom(\f0 -> dbg_write(f0, Num.to_str(num))) custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_u16 : U16 -> Inspector DbgFormatter dbg_u16 : U16 -> Inspector DbgFormatter
dbg_u16 = \num -> dbg_u16 = |num|
custom(\f0 -> dbg_write(f0, Num.to_str(num))) custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_i16 : I16 -> Inspector DbgFormatter dbg_i16 : I16 -> Inspector DbgFormatter
dbg_i16 = \num -> dbg_i16 = |num|
custom(\f0 -> dbg_write(f0, Num.to_str(num))) custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_u32 : U32 -> Inspector DbgFormatter dbg_u32 : U32 -> Inspector DbgFormatter
dbg_u32 = \num -> dbg_u32 = |num|
custom(\f0 -> dbg_write(f0, Num.to_str(num))) custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_i32 : I32 -> Inspector DbgFormatter dbg_i32 : I32 -> Inspector DbgFormatter
dbg_i32 = \num -> dbg_i32 = |num|
custom(\f0 -> dbg_write(f0, Num.to_str(num))) custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_u64 : U64 -> Inspector DbgFormatter dbg_u64 : U64 -> Inspector DbgFormatter
dbg_u64 = \num -> dbg_u64 = |num|
custom(\f0 -> dbg_write(f0, Num.to_str(num))) custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_i64 : I64 -> Inspector DbgFormatter dbg_i64 : I64 -> Inspector DbgFormatter
dbg_i64 = \num -> dbg_i64 = |num|
custom(\f0 -> dbg_write(f0, Num.to_str(num))) custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_u128 : U128 -> Inspector DbgFormatter dbg_u128 : U128 -> Inspector DbgFormatter
dbg_u128 = \num -> dbg_u128 = |num|
custom(\f0 -> dbg_write(f0, Num.to_str(num))) custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_i128 : I128 -> Inspector DbgFormatter dbg_i128 : I128 -> Inspector DbgFormatter
dbg_i128 = \num -> dbg_i128 = |num|
custom(\f0 -> dbg_write(f0, Num.to_str(num))) custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_f32 : F32 -> Inspector DbgFormatter dbg_f32 : F32 -> Inspector DbgFormatter
dbg_f32 = \num -> dbg_f32 = |num|
custom(\f0 -> dbg_write(f0, Num.to_str(num))) custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_f64 : F64 -> Inspector DbgFormatter dbg_f64 : F64 -> Inspector DbgFormatter
dbg_f64 = \num -> dbg_f64 = |num|
custom(\f0 -> dbg_write(f0, Num.to_str(num))) custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_dec : Dec -> Inspector DbgFormatter dbg_dec : Dec -> Inspector DbgFormatter
dbg_dec = \num -> dbg_dec = |num|
custom(\f0 -> dbg_write(f0, Num.to_str(num))) custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_write : DbgFormatter, Str -> DbgFormatter dbg_write : DbgFormatter, Str -> DbgFormatter
dbg_write = \@DbgFormatter({ data }), added -> dbg_write = |@DbgFormatter({ data }), added|
@DbgFormatter({ data: Str.concat(data, added) }) @DbgFormatter({ data: Str.concat(data, added) })
to_dbg_str : DbgFormatter -> Str to_dbg_str : DbgFormatter -> Str
to_dbg_str = \@DbgFormatter({ data }) -> data to_dbg_str = |@DbgFormatter({ data })| data

View file

@ -227,7 +227,7 @@ import Num exposing [U64, Num, U8]
## List.is_empty([]) ## List.is_empty([])
## ``` ## ```
is_empty : List * -> Bool is_empty : List * -> Bool
is_empty = \list -> is_empty = |list|
List.len(list) == 0 List.len(list) == 0
# unsafe primitive that does not perform a bounds check # unsafe primitive that does not perform a bounds check
@ -242,7 +242,7 @@ get_unsafe : List a, U64 -> a
## expect List.get([100, 200, 300], 5) == Err(OutOfBounds) ## expect List.get([100, 200, 300], 5) == Err(OutOfBounds)
## ``` ## ```
get : List a, U64 -> Result a [OutOfBounds] get : List a, U64 -> Result a [OutOfBounds]
get = \list, index -> get = |list, index|
if index < List.len(list) then if index < List.len(list) then
Ok(List.get_unsafe(list, index)) Ok(List.get_unsafe(list, index))
else else
@ -253,7 +253,7 @@ get = \list, index ->
replace_unsafe : List a, U64, a -> { list : List a, value : a } replace_unsafe : List a, U64, a -> { list : List a, value : a }
replace : List a, U64, a -> { list : List a, value : a } replace : List a, U64, a -> { list : List a, value : a }
replace = \list, index, new_value -> replace = |list, index, new_value|
if index < List.len(list) then if index < List.len(list) then
List.replace_unsafe(list, index, new_value) List.replace_unsafe(list, index, new_value)
else else
@ -268,7 +268,7 @@ replace = \list, index, new_value ->
## ##
## To drop the element at a given index, instead of replacing it, see [List.drop_at]. ## To drop the element at a given index, instead of replacing it, see [List.drop_at].
set : List a, U64, a -> List a set : List a, U64, a -> List a
set = \list, index, value -> set = |list, index, value|
(List.replace(list, index, value)).list (List.replace(list, index, value)).list
## Updates the element at the given index with the given function. ## Updates the element at the given index with the given function.
@ -281,7 +281,7 @@ set = \list, index, value ->
## To replace the element at a given index, instead of updating based on the current value, ## To replace the element at a given index, instead of updating based on the current value,
## see [List.set] and [List.replace] ## see [List.set] and [List.replace]
update : List a, U64, (a -> a) -> List a update : List a, U64, (a -> a) -> List a
update = \list, index, func -> update = |list, index, func|
when List.get(list, index) is when List.get(list, index) is
Err(OutOfBounds) -> list Err(OutOfBounds) -> list
Ok(value) -> Ok(value) ->
@ -292,7 +292,7 @@ update = \list, index, func ->
expect expect
list : List U64 list : List U64
list = [1, 2, 3] list = [1, 2, 3]
got = update(list, 1, \x -> x + 42) got = update(list, 1, |x| x + 42)
want = [1, 44, 3] want = [1, 44, 3]
got == want got == want
@ -300,7 +300,7 @@ expect
expect expect
list : List U64 list : List U64
list = [1, 2, 3] list = [1, 2, 3]
got = update(list, 5, \x -> x + 42) got = update(list, 5, |x| x + 42)
got == list got == list
# Update chain # Update chain
@ -309,9 +309,9 @@ expect
list = [1, 2, 3] list = [1, 2, 3]
got = got =
list list
|> update(0, \x -> x + 10) |> update(0, |x| x + 10)
|> update(1, \x -> x + 20) |> update(1, |x| x + 20)
|> update(2, \x -> x + 30) |> update(2, |x| x + 30)
want = [11, 22, 33] want = [11, 22, 33]
got == want got == want
@ -323,7 +323,7 @@ expect
## |> List.append(3) ## |> List.append(3)
## ``` ## ```
append : List a, a -> List a append : List a, a -> List a
append = \list, element -> append = |list, element|
list list
|> List.reserve(1) |> List.reserve(1)
|> List.append_unsafe(element) |> List.append_unsafe(element)
@ -338,7 +338,7 @@ append = \list, element ->
## |> List.append_if_ok(Err(3)) ## |> List.append_if_ok(Err(3))
## ``` ## ```
append_if_ok : List a, Result a * -> List a append_if_ok : List a, Result a * -> List a
append_if_ok = \list, result -> append_if_ok = |list, result|
when result is when result is
Ok(elem) -> append(list, elem) Ok(elem) -> append(list, elem)
Err(_) -> list Err(_) -> list
@ -369,7 +369,7 @@ prepend : List a, a -> List a
## |> List.prepend(Err(1)) ## |> List.prepend(Err(1))
## ``` ## ```
prepend_if_ok : List a, Result a * -> List a prepend_if_ok : List a, Result a * -> List a
prepend_if_ok = \list, result -> prepend_if_ok = |list, result|
when result is when result is
Ok(elem) -> prepend(list, elem) Ok(elem) -> prepend(list, elem)
Err(_) -> list Err(_) -> list
@ -405,7 +405,7 @@ concat : List a, List a -> List a
## expect List.last([]) == Err(ListWasEmpty) ## expect List.last([]) == Err(ListWasEmpty)
## ``` ## ```
last : List a -> Result a [ListWasEmpty] last : List a -> Result a [ListWasEmpty]
last = \list -> last = |list|
when List.get(list, Num.sub_saturated(List.len(list), 1)) is when List.get(list, Num.sub_saturated(List.len(list), 1)) is
Ok(v) -> Ok(v) Ok(v) -> Ok(v)
Err(_) -> Err(ListWasEmpty) Err(_) -> Err(ListWasEmpty)
@ -419,15 +419,15 @@ last = \list ->
## |> List.single ## |> List.single
## ``` ## ```
single : a -> List a single : a -> List a
single = \x -> [x] single = |x| [x]
## Returns a list with the given length, where every element is the given value. ## Returns a list with the given length, where every element is the given value.
repeat : a, U64 -> List a repeat : a, U64 -> List a
repeat = \value, count -> repeat = |value, count|
repeat_help(value, count, List.with_capacity(count)) repeat_help(value, count, List.with_capacity(count))
repeat_help : a, U64, List a -> List a repeat_help : a, U64, List a -> List a
repeat_help = \value, count, accum -> repeat_help = |value, count, accum|
if count > 0 then if count > 0 then
repeat_help(value, Num.sub_wrap(count, 1), List.append_unsafe(accum, value)) repeat_help(value, Num.sub_wrap(count, 1), List.append_unsafe(accum, value))
else else
@ -438,11 +438,11 @@ repeat_help = \value, count, accum ->
## expect List.reverse([1, 2, 3]) == [3, 2, 1] ## expect List.reverse([1, 2, 3]) == [3, 2, 1]
## ``` ## ```
reverse : List a -> List a reverse : List a -> List a
reverse = \list -> reverse = |list|
end = List.len(list) |> Num.sub_saturated(1) end = List.len(list) |> Num.sub_saturated(1)
reverse_help(List.clone(list), 0, end) reverse_help(List.clone(list), 0, end)
reverse_help = \list, left, right -> reverse_help = |list, left, right|
if left < right then if left < right then
reverse_help(List.swap(list, left, right), Num.add_wrap(left, 1), Num.sub_wrap(right, 1)) reverse_help(List.swap(list, left, right), Num.add_wrap(left, 1), Num.sub_wrap(right, 1))
else else
@ -458,15 +458,15 @@ clone : List a -> List a
## expect List.join([]) == [] ## expect List.join([]) == []
## ``` ## ```
join : List (List a) -> List a join : List (List a) -> List a
join = \lists -> join = |lists|
total_length = total_length =
List.walk(lists, 0, \state, list -> Num.add_wrap(state, List.len(list))) List.walk(lists, 0, |state, list| Num.add_wrap(state, List.len(list)))
List.walk(lists, List.with_capacity(total_length), \state, list -> List.concat(state, list)) List.walk(lists, List.with_capacity(total_length), |state, list| List.concat(state, list))
contains : List a, a -> Bool where a implements Eq contains : List a, a -> Bool where a implements Eq
contains = \list, needle -> contains = |list, needle|
List.any(list, \x -> x == needle) List.any(list, |x| x == needle)
## Build a value using each element in the list. ## Build a value using each element in the list.
## ##
@ -501,12 +501,12 @@ contains = \list, needle ->
## Note that in other languages, `walk` is sometimes called `reduce`, ## Note that in other languages, `walk` is sometimes called `reduce`,
## `fold`, `fold_left`, or `foldl`. ## `fold`, `fold_left`, or `foldl`.
walk : List elem, state, (state, elem -> state) -> state walk : List elem, state, (state, elem -> state) -> state
walk = \list, init, func -> walk = |list, init, func|
walk_help(list, init, func, 0, List.len(list)) walk_help(list, init, func, 0, List.len(list))
## internal helper ## internal helper
walk_help : List elem, s, (s, elem -> s), U64, U64 -> s walk_help : List elem, s, (s, elem -> s), U64, U64 -> s
walk_help = \list, state, f, index, length -> walk_help = |list, state, f, index, length|
if index < length then if index < length then
next_state = f(state, List.get_unsafe(list, index)) next_state = f(state, List.get_unsafe(list, index))
@ -516,12 +516,12 @@ walk_help = \list, state, f, index, length ->
## Like [walk], but at each step the function also receives the index of the current element. ## Like [walk], but at each step the function also receives the index of the current element.
walk_with_index : List elem, state, (state, elem, U64 -> state) -> state walk_with_index : List elem, state, (state, elem, U64 -> state) -> state
walk_with_index = \list, init, func -> walk_with_index = |list, init, func|
walk_with_index_help(list, init, func, 0, List.len(list)) walk_with_index_help(list, init, func, 0, List.len(list))
## internal helper ## internal helper
walk_with_index_help : List elem, s, (s, elem, U64 -> s), U64, U64 -> s walk_with_index_help : List elem, s, (s, elem, U64 -> s), U64, U64 -> s
walk_with_index_help = \list, state, f, index, length -> walk_with_index_help = |list, state, f, index, length|
if index < length then if index < length then
next_state = f(state, List.get_unsafe(list, index), index) next_state = f(state, List.get_unsafe(list, index), index)
@ -531,14 +531,14 @@ walk_with_index_help = \list, state, f, index, length ->
## Like [walk_until], but at each step the function also receives the index of the current element. ## Like [walk_until], 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 walk_with_index_until : List elem, state, (state, elem, U64 -> [Continue state, Break state]) -> state
walk_with_index_until = \list, state, f -> walk_with_index_until = |list, state, f|
when walk_with_index_until_help(list, state, f, 0, List.len(list)) is when walk_with_index_until_help(list, state, f, 0, List.len(list)) is
Continue(new) -> new Continue(new) -> new
Break(new) -> new Break(new) -> new
## internal helper ## internal helper
walk_with_index_until_help : List elem, s, (s, elem, U64 -> [Continue s, Break b]), U64, U64 -> [Continue s, Break b] walk_with_index_until_help : List elem, s, (s, elem, U64 -> [Continue s, Break b]), U64, U64 -> [Continue s, Break b]
walk_with_index_until_help = \list, state, f, index, length -> walk_with_index_until_help = |list, state, f, index, length|
if index < length then if index < length then
when f(state, List.get_unsafe(list, index), index) is when f(state, List.get_unsafe(list, index), index) is
Continue(next_state) -> Continue(next_state) ->
@ -551,12 +551,12 @@ walk_with_index_until_help = \list, state, f, index, length ->
## Note that in other languages, `walk_backwards` is sometimes called `reduce_right`, ## Note that in other languages, `walk_backwards` is sometimes called `reduce_right`,
## `fold`, `fold_right`, or `foldr`. ## `fold`, `fold_right`, or `foldr`.
walk_backwards : List elem, state, (state, elem -> state) -> state walk_backwards : List elem, state, (state, elem -> state) -> state
walk_backwards = \list, state, func -> walk_backwards = |list, state, func|
walk_backwards_help(list, state, func, len(list)) walk_backwards_help(list, state, func, len(list))
## internal helper ## internal helper
walk_backwards_help : List elem, state, (state, elem -> state), U64 -> state walk_backwards_help : List elem, state, (state, elem -> state), U64 -> state
walk_backwards_help = \list, state, f, index_plus_one -> walk_backwards_help = |list, state, f, index_plus_one|
if index_plus_one == 0 then if index_plus_one == 0 then
state state
else else
@ -577,47 +577,47 @@ walk_backwards_help = \list, state, f, index_plus_one ->
## As such, it is typically better for performance to use this over [List.walk] ## 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. ## if returning `Break` earlier than the last element is expected to be common.
walk_until : List elem, state, (state, elem -> [Continue state, Break state]) -> state walk_until : List elem, state, (state, elem -> [Continue state, Break state]) -> state
walk_until = \list, initial, step -> walk_until = |list, initial, step|
when List.iterate(list, initial, step) is when List.iterate(list, initial, step) is
Continue(new) -> new Continue(new) -> new
Break(new) -> new Break(new) -> new
## Same as [List.walk_until], but does it from the end of the list instead. ## Same as [List.walk_until], but does it from the end of the list instead.
walk_backwards_until : List elem, state, (state, elem -> [Continue state, Break state]) -> state walk_backwards_until : List elem, state, (state, elem -> [Continue state, Break state]) -> state
walk_backwards_until = \list, initial, func -> walk_backwards_until = |list, initial, func|
when List.iterate_backwards(list, initial, func) is when List.iterate_backwards(list, initial, func) is
Continue(new) -> new Continue(new) -> new
Break(new) -> new Break(new) -> new
## Walks to the end of the list from a specified starting index ## Walks to the end of the list from a specified starting index
walk_from : List elem, U64, state, (state, elem -> state) -> state walk_from : List elem, U64, state, (state, elem -> state) -> state
walk_from = \list, index, state, func -> walk_from = |list, index, state, func|
step : _, _ -> [Continue _, Break []] step : _, _ -> [Continue _, Break []]
step = \current_state, element -> Continue(func(current_state, element)) step = |current_state, element| Continue(func(current_state, element))
when List.iter_help(list, state, step, index, List.len(list)) is when List.iter_help(list, state, step, index, List.len(list)) is
Continue(new) -> new Continue(new) -> new
## A combination of [List.walk_from] and [List.walk_until] ## A combination of [List.walk_from] and [List.walk_until]
walk_from_until : List elem, U64, state, (state, elem -> [Continue state, Break state]) -> state walk_from_until : List elem, U64, state, (state, elem -> [Continue state, Break state]) -> state
walk_from_until = \list, index, state, func -> walk_from_until = |list, index, state, func|
when List.iter_help(list, state, func, index, List.len(list)) is when List.iter_help(list, state, func, index, List.len(list)) is
Continue(new) -> new Continue(new) -> new
Break(new) -> new Break(new) -> new
sum : List (Num a) -> Num a sum : List (Num a) -> Num a
sum = \list -> sum = |list|
List.walk(list, 0, Num.add) List.walk(list, 0, Num.add)
product : List (Num a) -> Num a product : List (Num a) -> Num a
product = \list -> product = |list|
List.walk(list, 1, Num.mul) List.walk(list, 1, Num.mul)
## Run the given predicate on each element of the list, returning `Bool.true` if ## Run the given predicate on each element of the list, returning `Bool.true` if
## any of the elements satisfy it. ## any of the elements satisfy it.
any : List a, (a -> Bool) -> Bool any : List a, (a -> Bool) -> Bool
any = \list, predicate -> any = |list, predicate|
looper = \{}, element -> looper = |{}, element|
if predicate(element) then if predicate(element) then
Break({}) Break({})
else else
@ -630,8 +630,8 @@ any = \list, predicate ->
## Run the given predicate on each element of the list, returning `Bool.true` if ## Run the given predicate on each element of the list, returning `Bool.true` if
## all of the elements satisfy it. ## all of the elements satisfy it.
all : List a, (a -> Bool) -> Bool all : List a, (a -> Bool) -> Bool
all = \list, predicate -> all = |list, predicate|
looper = \{}, element -> looper = |{}, element|
if predicate(element) then if predicate(element) then
Continue({}) Continue({})
else else
@ -663,13 +663,13 @@ all = \list, predicate ->
## list unaltered. ## list unaltered.
## ##
keep_if : List a, (a -> Bool) -> List a keep_if : List a, (a -> Bool) -> List a
keep_if = \list, predicate -> keep_if = |list, predicate|
length = List.len(list) length = List.len(list)
keep_if_help(list, predicate, 0, 0, length) keep_if_help(list, predicate, 0, 0, length)
keep_if_help : List a, (a -> Bool), U64, U64, U64 -> List a keep_if_help : List a, (a -> Bool), U64, U64, U64 -> List a
keep_if_help = \list, predicate, kept, index, length -> keep_if_help = |list, predicate, kept, index, length|
if index < length then if index < length then
if predicate(List.get_unsafe(list, index)) then if predicate(List.get_unsafe(list, index)) then
keep_if_help(List.swap(list, kept, index), predicate, Num.add_wrap(kept, 1), Num.add_wrap(index, 1), length) keep_if_help(List.swap(list, kept, index), predicate, Num.add_wrap(kept, 1), Num.add_wrap(index, 1), length)
@ -688,8 +688,8 @@ keep_if_help = \list, predicate, kept, index, length ->
## `List.drop_if` has the same performance characteristics as [List.keep_if]. ## `List.drop_if` has the same performance characteristics as [List.keep_if].
## See its documentation for details on those characteristics! ## See its documentation for details on those characteristics!
drop_if : List a, (a -> Bool) -> List a drop_if : List a, (a -> Bool) -> List a
drop_if = \list, predicate -> drop_if = |list, predicate|
List.keep_if(list, \e -> Bool.not(predicate(e))) List.keep_if(list, |e| Bool.not(predicate(e)))
## Run the given function on each element of a list, and return the ## Run the given function on each element of a list, and return the
## number of elements for which the function returned `Bool.true`. ## number of elements for which the function returned `Bool.true`.
@ -698,8 +698,8 @@ drop_if = \list, predicate ->
## expect List.count_if([1, 2, 3], (\num -> num > 1)) == 2 ## expect List.count_if([1, 2, 3], (\num -> num > 1)) == 2
## ``` ## ```
count_if : List a, (a -> Bool) -> U64 count_if : List a, (a -> Bool) -> U64
count_if = \list, predicate -> count_if = |list, predicate|
walk_state = \state, elem -> walk_state = |state, elem|
if predicate(elem) then if predicate(elem) then
Num.add_wrap(state, 1) Num.add_wrap(state, 1)
else else
@ -718,8 +718,8 @@ count_if = \list, predicate ->
## expect List.keep_oks(["", "a", "bc", "", "d", "ef", ""], fn) == ["a", "bc", "d", "ef"] ## expect List.keep_oks(["", "a", "bc", "", "d", "ef", ""], fn) == ["a", "bc", "d", "ef"]
## ``` ## ```
keep_oks : List before, (before -> Result after *) -> List after keep_oks : List before, (before -> Result after *) -> List after
keep_oks = \list, to_result -> keep_oks = |list, to_result|
walker = \accum, element -> walker = |accum, element|
when to_result(element) is when to_result(element) is
Ok(keep) -> List.append(accum, keep) Ok(keep) -> List.append(accum, keep)
Err(_drop) -> accum Err(_drop) -> accum
@ -736,8 +736,8 @@ keep_oks = \list, to_result ->
## List.keep_errs(["", "a", "bc", "", "d", "ef", ""], fn) ## List.keep_errs(["", "a", "bc", "", "d", "ef", ""], fn)
## ``` ## ```
keep_errs : List before, (before -> Result * after) -> List after keep_errs : List before, (before -> Result * after) -> List after
keep_errs = \list, to_result -> keep_errs = |list, to_result|
walker = \accum, element -> walker = |accum, element|
when to_result(element) is when to_result(element) is
Ok(_drop) -> accum Ok(_drop) -> accum
Err(keep) -> List.append(accum, keep) Err(keep) -> List.append(accum, keep)
@ -752,14 +752,14 @@ keep_errs = \list, to_result ->
## expect List.map(["", "a", "bc"], Str.is_empty) == [Bool.true, Bool.false, Bool.false] ## expect List.map(["", "a", "bc"], Str.is_empty) == [Bool.true, Bool.false, Bool.false]
## ``` ## ```
map : List a, (a -> b) -> List b map : List a, (a -> b) -> List b
map = \list, mapper -> map = |list, mapper|
# TODO: allow checking the refcounting and running the map inplace. # TODO: allow checking the refcounting and running the map inplace.
# Preferably allow it even if the types are different (must be same size with padding though). # Preferably allow it even if the types are different (must be same size with padding though).
length = List.len(list) length = List.len(list)
List.walk( List.walk(
list, list,
List.with_capacity(length), List.with_capacity(length),
\state, elem -> |state, elem|
List.append_unsafe(state, mapper(elem)), List.append_unsafe(state, mapper(elem)),
) )
@ -773,12 +773,12 @@ map = \list, mapper ->
## zipped = List.map2(["a", "b", "c"], [1, 2, 3], Pair) ## zipped = List.map2(["a", "b", "c"], [1, 2, 3], Pair)
## ``` ## ```
map2 : List a, List b, (a, b -> c) -> List c map2 : List a, List b, (a, b -> c) -> List c
map2 = \list_a, list_b, mapper -> map2 = |list_a, list_b, mapper|
length = Num.min(List.len(list_a), List.len(list_b)) length = Num.min(List.len(list_a), List.len(list_b))
map2_help(list_a, list_b, List.with_capacity(length), mapper, 0, length) map2_help(list_a, list_b, List.with_capacity(length), mapper, 0, length)
map2_help : List a, List b, List c, (a, b -> c), U64, U64 -> List c map2_help : List a, List b, List c, (a, b -> c), U64, U64 -> List c
map2_help = \list_a, list_b, out, mapper, index, length -> map2_help = |list_a, list_b, out, mapper, index, length|
if index < length then if index < length then
mapped = mapper(List.get_unsafe(list_a, index), List.get_unsafe(list_b, index)) mapped = mapper(List.get_unsafe(list_a, index), List.get_unsafe(list_b, index))
@ -790,7 +790,7 @@ map2_help = \list_a, list_b, out, mapper, index, length ->
## and use that as the first element in the returned list. ## and use that as the first element in the returned list.
## Repeat until a list runs out of elements. ## Repeat until a list runs out of elements.
map3 : List a, List b, List c, (a, b, c -> d) -> List d map3 : List a, List b, List c, (a, b, c -> d) -> List d
map3 = \list_a, list_b, list_c, mapper -> map3 = |list_a, list_b, list_c, mapper|
length = Num.min( length = Num.min(
Num.min(List.len(list_a), List.len(list_b)), Num.min(List.len(list_a), List.len(list_b)),
List.len(list_c), List.len(list_c),
@ -798,7 +798,7 @@ map3 = \list_a, list_b, list_c, mapper ->
map3_help(list_a, list_b, list_c, List.with_capacity(length), mapper, 0, length) map3_help(list_a, list_b, list_c, List.with_capacity(length), mapper, 0, length)
map3_help : List a, List b, List c, List d, (a, b, c -> d), U64, U64 -> List d map3_help : List a, List b, List c, List d, (a, b, c -> d), U64, U64 -> List d
map3_help = \list_a, list_b, list_c, out, mapper, index, length -> map3_help = |list_a, list_b, list_c, out, mapper, index, length|
if index < length then if index < length then
mapped = mapper(List.get_unsafe(list_a, index), List.get_unsafe(list_b, index), List.get_unsafe(list_c, index)) mapped = mapper(List.get_unsafe(list_a, index), List.get_unsafe(list_b, index), List.get_unsafe(list_c, index))
@ -810,7 +810,7 @@ map3_help = \list_a, list_b, list_c, out, mapper, index, length ->
## and use that as the first element in the returned list. ## and use that as the first element in the returned list.
## Repeat until a list runs out of elements. ## Repeat until a list runs out of elements.
map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e
map4 = \list_a, list_b, list_c, list_d, mapper -> map4 = |list_a, list_b, list_c, list_d, mapper|
length = Num.min( length = Num.min(
Num.min(List.len(list_a), List.len(list_b)), Num.min(List.len(list_a), List.len(list_b)),
Num.min(List.len(list_c), List.len(list_d)), Num.min(List.len(list_c), List.len(list_d)),
@ -818,7 +818,7 @@ map4 = \list_a, list_b, list_c, list_d, mapper ->
map4_help(list_a, list_b, list_c, list_d, List.with_capacity(length), mapper, 0, length) map4_help(list_a, list_b, list_c, list_d, List.with_capacity(length), mapper, 0, length)
map4_help : List a, List b, List c, List d, List e, (a, b, c, d -> e), U64, U64 -> List e map4_help : List a, List b, List c, List d, List e, (a, b, c, d -> e), U64, U64 -> List e
map4_help = \list_a, list_b, list_c, list_d, out, mapper, index, length -> map4_help = |list_a, list_b, list_c, list_d, out, mapper, index, length|
if index < length then if index < length then
mapped = mapper(List.get_unsafe(list_a, index), List.get_unsafe(list_b, index), List.get_unsafe(list_c, index), List.get_unsafe(list_d, index)) mapped = mapper(List.get_unsafe(list_a, index), List.get_unsafe(list_b, index), List.get_unsafe(list_c, index), List.get_unsafe(list_d, index))
@ -832,7 +832,7 @@ map4_help = \list_a, list_b, list_c, list_d, out, mapper, index, length ->
## expect List.map_with_index([10, 20, 30], (\num, index -> num + index)) == [10, 21, 32] ## expect List.map_with_index([10, 20, 30], (\num, index -> num + index)) == [10, 21, 32]
## ``` ## ```
map_with_index : List a, (a, U64 -> b) -> List b map_with_index : List a, (a, U64 -> b) -> List b
map_with_index = \src, func -> map_with_index = |src, func|
length = len(src) length = len(src)
dest = with_capacity(length) dest = with_capacity(length)
@ -840,7 +840,7 @@ map_with_index = \src, func ->
# Internal helper # Internal helper
map_with_index_help : List a, List b, (a, U64 -> b), U64, U64 -> List b map_with_index_help : List a, List b, (a, U64 -> b), U64, U64 -> List b
map_with_index_help = \src, dest, func, index, length -> map_with_index_help = |src, dest, func, index, length|
if index < length then if index < length then
elem = get_unsafe(src, index) elem = get_unsafe(src, index)
mapped_elem = func(elem, index) mapped_elem = func(elem, index)
@ -875,30 +875,30 @@ map_with_index_help = \src, dest, func, index, length ->
## All of these options are compatible with the others. For example, you can use `At` or `After` ## 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. ## with `start` regardless of what `end` and `step` are set to.
range : _ range : _
range = \{ start, end, step ?? 0 } -> range = |{ start, end, step ?? 0 }|
{ calc_next, step_is_positive } = { calc_next, step_is_positive } =
if step == 0 then if step == 0 then
when T(start, end) is when T(start, end) is
T(At(x), At(y)) | T(At(x), Before(y)) | T(After(x), At(y)) | T(After(x), Before(y)) -> T(At(x), At(y)) | T(At(x), Before(y)) | T(After(x), At(y)) | T(After(x), Before(y)) ->
if x < y then if x < y then
{ {
calc_next: \i -> Num.add_checked(i, 1), calc_next: |i| Num.add_checked(i, 1),
step_is_positive: Bool.true, step_is_positive: Bool.true,
} }
else else
{ {
calc_next: \i -> Num.sub_checked(i, 1), calc_next: |i| Num.sub_checked(i, 1),
step_is_positive: Bool.false, step_is_positive: Bool.false,
} }
T(At(_), Length(_)) | T(After(_), Length(_)) -> T(At(_), Length(_)) | T(After(_), Length(_)) ->
{ {
calc_next: \i -> Num.add_checked(i, 1), calc_next: |i| Num.add_checked(i, 1),
step_is_positive: Bool.true, step_is_positive: Bool.true,
} }
else else
{ {
calc_next: \i -> Num.add_checked(i, step), calc_next: |i| Num.add_checked(i, step),
step_is_positive: step > 0, step_is_positive: step > 0,
} }
@ -911,9 +911,9 @@ range = \{ start, end, step ?? 0 } ->
At(at) -> At(at) ->
is_valid = is_valid =
if step_is_positive then if step_is_positive then
\i -> i <= at |i| i <= at
else else
\i -> i >= at |i| i >= at
# TODO: switch to List.with_capacity # TODO: switch to List.with_capacity
range_help([], inclusive_start, calc_next, is_valid) range_help([], inclusive_start, calc_next, is_valid)
@ -921,9 +921,9 @@ range = \{ start, end, step ?? 0 } ->
Before(before) -> Before(before) ->
is_valid = is_valid =
if step_is_positive then if step_is_positive then
\i -> i < before |i| i < before
else else
\i -> i > before |i| i > before
# TODO: switch to List.with_capacity # TODO: switch to List.with_capacity
range_help([], inclusive_start, calc_next, is_valid) range_help([], inclusive_start, calc_next, is_valid)
@ -931,7 +931,7 @@ range = \{ start, end, step ?? 0 } ->
Length(l) -> Length(l) ->
range_length_help(List.with_capacity(l), inclusive_start, l, calc_next) range_length_help(List.with_capacity(l), inclusive_start, l, calc_next)
range_help = \accum, i, calc_next, is_valid -> range_help = |accum, i, calc_next, is_valid|
when i is when i is
Ok(val) -> Ok(val) ->
if is_valid(val) then if is_valid(val) then
@ -945,7 +945,7 @@ range_help = \accum, i, calc_next, is_valid ->
# return the generated list. # return the generated list.
accum accum
range_length_help = \accum, i, remaining, calc_next -> range_length_help = |accum, i, remaining, calc_next|
if remaining == 0 then if remaining == 0 then
accum accum
else else
@ -1004,19 +1004,19 @@ sort_with : List a, (a, a -> [LT, EQ, GT]) -> List a
## ##
## To sort in descending order (highest to lowest), use [List.sort_desc] instead. ## To sort in descending order (highest to lowest), use [List.sort_desc] instead.
sort_asc : List (Num a) -> List (Num a) sort_asc : List (Num a) -> List (Num a)
sort_asc = \list -> List.sort_with(list, Num.compare) sort_asc = |list| List.sort_with(list, Num.compare)
## Sorts a list of numbers in descending order (highest to lowest). ## Sorts a list of numbers in descending order (highest to lowest).
## ##
## To sort in ascending order (lowest to highest), use [List.sort_asc] instead. ## To sort in ascending order (lowest to highest), use [List.sort_asc] instead.
sort_desc : List (Num a) -> List (Num a) sort_desc : List (Num a) -> List (Num a)
sort_desc = \list -> List.sort_with(list, \a, b -> Num.compare(b, a)) sort_desc = |list| List.sort_with(list, |a, b| Num.compare(b, a))
swap : List a, U64, U64 -> List a swap : List a, U64, U64 -> List a
## Returns the first element in the list, or `ListWasEmpty` if it was empty. ## Returns the first element in the list, or `ListWasEmpty` if it was empty.
first : List a -> Result a [ListWasEmpty] first : List a -> Result a [ListWasEmpty]
first = \list -> first = |list|
when List.get(list, 0) is when List.get(list, 0) is
Ok(v) -> Ok(v) Ok(v) -> Ok(v)
Err(_) -> Err(ListWasEmpty) Err(_) -> Err(ListWasEmpty)
@ -1038,7 +1038,7 @@ first = \list ->
## To split the list into two lists, use `List.split_at`. ## To split the list into two lists, use `List.split_at`.
## ##
take_first : List elem, U64 -> List elem take_first : List elem, U64 -> List elem
take_first = \list, output_length -> take_first = |list, output_length|
List.sublist(list, { start: 0, len: output_length }) List.sublist(list, { start: 0, len: output_length })
## Returns the given number of elements from the end of the list. ## Returns the given number of elements from the end of the list.
@ -1058,19 +1058,19 @@ take_first = \list, output_length ->
## To split the list into two lists, use `List.split_at`. ## To split the list into two lists, use `List.split_at`.
## ##
take_last : List elem, U64 -> List elem take_last : List elem, U64 -> List elem
take_last = \list, output_length -> take_last = |list, output_length|
List.sublist(list, { start: Num.sub_saturated(List.len(list), output_length), len: output_length }) List.sublist(list, { start: Num.sub_saturated(List.len(list), output_length), len: output_length })
## Drops n elements from the beginning of the list. ## Drops n elements from the beginning of the list.
drop_first : List elem, U64 -> List elem drop_first : List elem, U64 -> List elem
drop_first = \list, n -> drop_first = |list, n|
remaining = Num.sub_saturated(List.len(list), n) remaining = Num.sub_saturated(List.len(list), n)
List.take_last(list, remaining) List.take_last(list, remaining)
## Drops n elements from the end of the list. ## Drops n elements from the end of the list.
drop_last : List elem, U64 -> List elem drop_last : List elem, U64 -> List elem
drop_last = \list, n -> drop_last = |list, n|
remaining = Num.sub_saturated(List.len(list), n) remaining = Num.sub_saturated(List.len(list), n)
List.take_first(list, remaining) List.take_first(list, remaining)
@ -1083,7 +1083,7 @@ drop_last = \list, n ->
drop_at : List elem, U64 -> List elem drop_at : List elem, U64 -> List elem
min : List (Num a) -> Result (Num a) [ListWasEmpty] min : List (Num a) -> Result (Num a) [ListWasEmpty]
min = \list -> min = |list|
when List.first(list) is when List.first(list) is
Ok(initial) -> Ok(initial) ->
Ok(min_help(list, initial)) Ok(min_help(list, initial))
@ -1092,11 +1092,11 @@ min = \list ->
Err(ListWasEmpty) Err(ListWasEmpty)
min_help : List (Num a), Num a -> Num a min_help : List (Num a), Num a -> Num a
min_help = \list, initial -> min_help = |list, initial|
List.walk( List.walk(
list, list,
initial, initial,
\best_so_far, current -> |best_so_far, current|
if current < best_so_far then if current < best_so_far then
current current
else else
@ -1104,7 +1104,7 @@ min_help = \list, initial ->
) )
max : List (Num a) -> Result (Num a) [ListWasEmpty] max : List (Num a) -> Result (Num a) [ListWasEmpty]
max = \list -> max = |list|
when List.first(list) is when List.first(list) is
Ok(initial) -> Ok(initial) ->
Ok(max_help(list, initial)) Ok(max_help(list, initial))
@ -1113,11 +1113,11 @@ max = \list ->
Err(ListWasEmpty) Err(ListWasEmpty)
max_help : List (Num a), Num a -> Num a max_help : List (Num a), Num a -> Num a
max_help = \list, initial -> max_help = |list, initial|
List.walk( List.walk(
list, list,
initial, initial,
\best_so_far, current -> |best_so_far, current|
if current > best_so_far then if current > best_so_far then
current current
else else
@ -1129,14 +1129,14 @@ max_help = \list, initial ->
## ##
## You may know a similar function named `concat_map` in other languages. ## You may know a similar function named `concat_map` in other languages.
join_map : List a, (a -> List b) -> List b join_map : List a, (a -> List b) -> List b
join_map = \list, mapper -> join_map = |list, mapper|
List.walk(list, [], \state, elem -> List.concat(state, mapper(elem))) List.walk(list, [], |state, elem| List.concat(state, mapper(elem)))
## Returns the first element of the list satisfying a predicate function. ## Returns the first element of the list satisfying a predicate function.
## If no satisfying element is found, an `Err NotFound` is returned. ## If no satisfying element is found, an `Err NotFound` is returned.
find_first : List elem, (elem -> Bool) -> Result elem [NotFound] find_first : List elem, (elem -> Bool) -> Result elem [NotFound]
find_first = \list, pred -> find_first = |list, pred|
callback = \_, elem -> callback = |_, elem|
if pred(elem) then if pred(elem) then
Break(elem) Break(elem)
else else
@ -1149,8 +1149,8 @@ find_first = \list, pred ->
## Returns the last element of the list satisfying a predicate function. ## Returns the last element of the list satisfying a predicate function.
## If no satisfying element is found, an `Err NotFound` is returned. ## If no satisfying element is found, an `Err NotFound` is returned.
find_last : List elem, (elem -> Bool) -> Result elem [NotFound] find_last : List elem, (elem -> Bool) -> Result elem [NotFound]
find_last = \list, pred -> find_last = |list, pred|
callback = \_, elem -> callback = |_, elem|
if pred(elem) then if pred(elem) then
Break(elem) Break(elem)
else else
@ -1164,11 +1164,11 @@ find_last = \list, pred ->
## satisfying a predicate function can be found. ## satisfying a predicate function can be found.
## If no satisfying element is found, an `Err NotFound` is returned. ## If no satisfying element is found, an `Err NotFound` is returned.
find_first_index : List elem, (elem -> Bool) -> Result U64 [NotFound] find_first_index : List elem, (elem -> Bool) -> Result U64 [NotFound]
find_first_index = \list, matcher -> find_first_index = |list, matcher|
found_index = List.iterate( found_index = List.iterate(
list, list,
0, 0,
\index, elem -> |index, elem|
if matcher(elem) then if matcher(elem) then
Break(index) Break(index)
else else
@ -1183,11 +1183,11 @@ find_first_index = \list, matcher ->
## satisfying a predicate function can be found. ## satisfying a predicate function can be found.
## If no satisfying element is found, an `Err NotFound` is returned. ## If no satisfying element is found, an `Err NotFound` is returned.
find_last_index : List elem, (elem -> Bool) -> Result U64 [NotFound] find_last_index : List elem, (elem -> Bool) -> Result U64 [NotFound]
find_last_index = \list, matches -> find_last_index = |list, matches|
found_index = List.iterate_backwards( found_index = List.iterate_backwards(
list, list,
List.len(list), List.len(list),
\prev_index, elem -> |prev_index, elem|
answer = Num.sub_wrap(prev_index, 1) answer = Num.sub_wrap(prev_index, 1)
if matches(elem) then if matches(elem) then
@ -1216,7 +1216,7 @@ find_last_index = \list, matches ->
## ##
## Some languages have a function called **`slice`** which works similarly to this. ## Some languages have a function called **`slice`** which works similarly to this.
sublist : List elem, { start : U64, len : U64 } -> List elem sublist : List elem, { start : U64, len : U64 } -> List elem
sublist = \list, config -> sublist = |list, config|
sublist_lowlevel(list, config.start, config.len) sublist_lowlevel(list, config.start, config.len)
## low-level slicing operation that does no bounds checking ## low-level slicing operation that does no bounds checking
@ -1227,14 +1227,14 @@ sublist_lowlevel : List elem, U64, U64 -> List elem
## List.intersperse([1, 2, 3], 9) == [1, 9, 2, 9, 3] ## List.intersperse([1, 2, 3], 9) == [1, 9, 2, 9, 3]
## ``` ## ```
intersperse : List elem, elem -> List elem intersperse : List elem, elem -> List elem
intersperse = \list, sep -> intersperse = |list, sep|
capacity = 2 * List.len(list) capacity = 2 * List.len(list)
init = List.with_capacity(capacity) init = List.with_capacity(capacity)
new_list = new_list =
List.walk( List.walk(
list, list,
init, init,
\acc, elem -> |acc, elem|
acc acc
|> List.append_unsafe(elem) |> List.append_unsafe(elem)
|> List.append_unsafe(sep), |> List.append_unsafe(sep),
@ -1249,7 +1249,7 @@ intersperse = \list, sep ->
## ##
## If the first list is empty, this only returns `Bool.true` if the second list is empty. ## If the first list is empty, this only returns `Bool.true` if the second list is empty.
starts_with : List elem, List elem -> Bool where elem implements Eq starts_with : List elem, List elem -> Bool where elem implements Eq
starts_with = \list, prefix -> starts_with = |list, prefix|
# TODO once we have seamless slices, verify that this wouldn't # TODO once we have seamless slices, verify that this wouldn't
# have better performance with a function like List.compare_sublists # have better performance with a function like List.compare_sublists
prefix == List.sublist(list, { start: 0, len: List.len(prefix) }) prefix == List.sublist(list, { start: 0, len: List.len(prefix) })
@ -1261,7 +1261,7 @@ starts_with = \list, prefix ->
## ##
## If the first list is empty, this only returns `Bool.true` if the second list is empty. ## 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 ends_with : List elem, List elem -> Bool where elem implements Eq
ends_with = \list, suffix -> ends_with = |list, suffix|
# TODO once we have seamless slices, verify that this wouldn't # TODO once we have seamless slices, verify that this wouldn't
# have better performance with a function like List.compare_sublists # have better performance with a function like List.compare_sublists
length = List.len(suffix) length = List.len(suffix)
@ -1277,7 +1277,7 @@ ends_with = \list, suffix ->
## means if you give an index of 0, the `before` list will be empty and the ## 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.) ## `others` list will have the same elements as the original list.)
split_at : List elem, U64 -> { before : List elem, others : List elem } split_at : List elem, U64 -> { before : List elem, others : List elem }
split_at = \elements, user_split_index -> split_at = |elements, user_split_index|
length = List.len(elements) length = List.len(elements)
split_index = if length > user_split_index then user_split_index else length split_index = if length > user_split_index then user_split_index else length
before = List.sublist(elements, { start: 0, len: split_index }) before = List.sublist(elements, { start: 0, len: split_index })
@ -1291,8 +1291,8 @@ split_at = \elements, user_split_index ->
## List.split_on([1, 2, 3], 2) == [[1], [3]] ## List.split_on([1, 2, 3], 2) == [[1], [3]]
## ``` ## ```
split_on : List a, a -> List (List a) where a implements Eq split_on : List a, a -> List (List a) where a implements Eq
split_on = \elements, delimiter -> split_on = |elements, delimiter|
help = \remaining, chunks, current_chunk -> help = |remaining, chunks, current_chunk|
when remaining is when remaining is
[] -> List.append(chunks, current_chunk) [] -> List.append(chunks, current_chunk)
[x, .. as rest] if x == delimiter -> [x, .. as rest] if x == delimiter ->
@ -1308,8 +1308,8 @@ split_on = \elements, delimiter ->
## List.split_on_list([1, 2, 3], [1, 2]) == [[], [3]] ## List.split_on_list([1, 2, 3], [1, 2]) == [[], [3]]
## ``` ## ```
split_on_list : List a, List a -> List (List a) where a implements Eq split_on_list : List a, List a -> List (List a) where a implements Eq
split_on_list = \elements, delimiter -> split_on_list = |elements, delimiter|
help = \remaining, chunks, current_chunk -> help = |remaining, chunks, current_chunk|
when remaining is when remaining is
[] -> List.append(chunks, current_chunk) [] -> List.append(chunks, current_chunk)
[x, .. as rest] -> [x, .. as rest] ->
@ -1329,8 +1329,8 @@ split_on_list = \elements, delimiter ->
## List.split_first([Foo, Z, Bar, Z, Baz], Z) == Ok({ before: [Foo], after: [Bar, Z, Baz] }) ## List.split_first([Foo, Z, Bar, Z, Baz], Z) == Ok({ before: [Foo], after: [Bar, Z, Baz] })
## ``` ## ```
split_first : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] where elem implements Eq split_first : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] where elem implements Eq
split_first = \list, delimiter -> split_first = |list, delimiter|
when List.find_first_index(list, \elem -> elem == delimiter) is when List.find_first_index(list, |elem| elem == delimiter) is
Ok(index) -> Ok(index) ->
before = List.sublist(list, { start: 0, len: index }) before = List.sublist(list, { start: 0, len: index })
after = List.sublist(list, { start: Num.add_wrap(index, 1), len: Num.sub_wrap(List.len(list), index) |> Num.sub_wrap(1) }) after = List.sublist(list, { start: Num.add_wrap(index, 1), len: Num.sub_wrap(List.len(list), index) |> Num.sub_wrap(1) })
@ -1345,8 +1345,8 @@ split_first = \list, delimiter ->
## List.split_last([Foo, Z, Bar, Z, Baz], Z) == Ok({ before: [Foo, Z, Bar], after: [Baz] }) ## List.split_last([Foo, Z, Bar, Z, Baz], Z) == Ok({ before: [Foo, Z, Bar], after: [Baz] })
## ``` ## ```
split_last : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] where elem implements Eq split_last : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] where elem implements Eq
split_last = \list, delimiter -> split_last = |list, delimiter|
when List.find_last_index(list, \elem -> elem == delimiter) is when List.find_last_index(list, |elem| elem == delimiter) is
Ok(index) -> Ok(index) ->
before = List.sublist(list, { start: 0, len: index }) before = List.sublist(list, { start: 0, len: index })
after = List.sublist(list, { start: Num.add_wrap(index, 1), len: Num.sub_wrap(List.len(list), index) |> Num.sub_wrap(1) }) after = List.sublist(list, { start: Num.add_wrap(index, 1), len: Num.sub_wrap(List.len(list), index) |> Num.sub_wrap(1) })
@ -1360,7 +1360,7 @@ split_last = \list, delimiter ->
## chunk size. If the provided list is empty or if the chunk size is 0 then the ## chunk size. If the provided list is empty or if the chunk size is 0 then the
## result is an empty list. ## result is an empty list.
chunks_of : List a, U64 -> List (List a) chunks_of : List a, U64 -> List (List a)
chunks_of = \list, chunk_size -> chunks_of = |list, chunk_size|
if chunk_size == 0 || List.is_empty(list) then if chunk_size == 0 || List.is_empty(list) then
[] []
else else
@ -1368,7 +1368,7 @@ chunks_of = \list, chunk_size ->
chunks_of_help(list, chunk_size, List.with_capacity(chunk_capacity)) chunks_of_help(list, chunk_size, List.with_capacity(chunk_capacity))
chunks_of_help : List a, U64, List (List a) -> List (List a) chunks_of_help : List a, U64, List (List a) -> List (List a)
chunks_of_help = \list_rest, chunk_size, chunks -> chunks_of_help = |list_rest, chunk_size, chunks|
if List.is_empty(list_rest) then if List.is_empty(list_rest) then
chunks chunks
else else
@ -1379,14 +1379,14 @@ chunks_of_help = \list_rest, chunk_size, chunks ->
## If that function ever returns `Err`, [map_try] immediately returns that `Err`. ## 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. ## 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 map_try : List elem, (elem -> Result ok err) -> Result (List ok) err
map_try = \list, to_result -> map_try = |list, to_result|
walk_try( walk_try(
list, list,
[], [],
\state, elem -> |state, elem|
Result.map( Result.map_ok(
to_result(elem), to_result(elem),
\ok -> |ok|
List.append(state, ok), List.append(state, ok),
), ),
) )
@ -1403,12 +1403,12 @@ map_try = \list, to_result ->
## As such, it is typically better for performance to use this over [List.walk] ## 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. ## if returning `Break` earlier than the last element is expected to be common.
walk_try : List elem, state, (state, elem -> Result state err) -> Result state err walk_try : List elem, state, (state, elem -> Result state err) -> Result state err
walk_try = \list, init, func -> walk_try = |list, init, func|
walk_try_help(list, init, func, 0, List.len(list)) walk_try_help(list, init, func, 0, List.len(list))
## internal helper ## internal helper
walk_try_help : List elem, state, (state, elem -> Result state err), U64, U64 -> Result state err walk_try_help : List elem, state, (state, elem -> Result state err), U64, U64 -> Result state err
walk_try_help = \list, state, f, index, length -> walk_try_help = |list, state, f, index, length|
if index < length then if index < length then
when f(state, List.get_unsafe(list, index)) is when f(state, List.get_unsafe(list, index)) is
Ok(next_state) -> walk_try_help(list, next_state, f, Num.add_wrap(index, 1), length) Ok(next_state) -> walk_try_help(list, next_state, f, Num.add_wrap(index, 1), length)
@ -1418,12 +1418,12 @@ walk_try_help = \list, state, f, index, length ->
## Primitive for iterating over a List, being able to decide at every element whether to continue ## Primitive for iterating over a List, being able to decide at every element whether to continue
iterate : List elem, s, (s, elem -> [Continue s, Break b]) -> [Continue s, Break b] iterate : List elem, s, (s, elem -> [Continue s, Break b]) -> [Continue s, Break b]
iterate = \list, init, func -> iterate = |list, init, func|
iter_help(list, init, func, 0, List.len(list)) iter_help(list, init, func, 0, List.len(list))
## internal helper ## internal helper
iter_help : List elem, s, (s, elem -> [Continue s, Break b]), U64, U64 -> [Continue s, Break b] iter_help : List elem, s, (s, elem -> [Continue s, Break b]), U64, U64 -> [Continue s, Break b]
iter_help = \list, state, f, index, length -> iter_help = |list, state, f, index, length|
if index < length then if index < length then
when f(state, List.get_unsafe(list, index)) is when f(state, List.get_unsafe(list, index)) is
Continue(next_state) -> iter_help(list, next_state, f, Num.add_wrap(index, 1), length) Continue(next_state) -> iter_help(list, next_state, f, Num.add_wrap(index, 1), length)
@ -1434,12 +1434,12 @@ iter_help = \list, state, f, index, length ->
## Primitive for iterating over a List from back to front, being able to decide at every ## Primitive for iterating over a List from back to front, being able to decide at every
## element whether to continue ## element whether to continue
iterate_backwards : List elem, s, (s, elem -> [Continue s, Break b]) -> [Continue s, Break b] iterate_backwards : List elem, s, (s, elem -> [Continue s, Break b]) -> [Continue s, Break b]
iterate_backwards = \list, init, func -> iterate_backwards = |list, init, func|
iter_backwards_help(list, init, func, List.len(list)) iter_backwards_help(list, init, func, List.len(list))
## internal helper ## internal helper
iter_backwards_help : List elem, s, (s, elem -> [Continue s, Break b]), U64 -> [Continue s, Break b] iter_backwards_help : List elem, s, (s, elem -> [Continue s, Break b]), U64 -> [Continue s, Break b]
iter_backwards_help = \list, state, f, prev_index -> iter_backwards_help = |list, state, f, prev_index|
if prev_index > 0 then if prev_index > 0 then
index = Num.sub_wrap(prev_index, 1) index = Num.sub_wrap(prev_index, 1)
@ -1468,7 +1468,7 @@ expect (List.concat_utf8([1, 2, 3, 4], "🐦")) == [1, 2, 3, 4, 240, 159, 144, 1
## ##
## If the function might fail or you need to return early, use [for_each_try!]. ## If the function might fail or you need to return early, use [for_each_try!].
for_each! : List a, (a => {}) => {} for_each! : List a, (a => {}) => {}
for_each! = \list, func! -> for_each! = |list, func!|
when list is when list is
[] -> [] ->
{} {}
@ -1489,7 +1489,7 @@ for_each! = \list, func! ->
## ) ## )
## ``` ## ```
for_each_try! : List a, (a => Result {} err) => Result {} err for_each_try! : List a, (a => Result {} err) => Result {} err
for_each_try! = \list, func! -> for_each_try! = |list, func!|
when list is when list is
[] -> [] ->
Ok({}) Ok({})
@ -1513,7 +1513,7 @@ for_each_try! = \list, func! ->
## ##
## This is the same as [walk], except that the step function can have effects. ## This is the same as [walk], except that the step function can have effects.
walk! : List elem, state, (state, elem => state) => state walk! : List elem, state, (state, elem => state) => state
walk! = \list, state, func! -> walk! = |list, state, func!|
when list is when list is
[] -> [] ->
state state
@ -1540,7 +1540,7 @@ walk! = \list, state, func! ->
## ##
## This is the same as [walk_try], except that the step function can have effects. ## This is the same as [walk_try], except that the step function can have effects.
walk_try! : List elem, state, (state, elem => Result state err) => Result state err walk_try! : List elem, state, (state, elem => Result state err) => Result state err
walk_try! = \list, state, func! -> walk_try! = |list, state, func!|
when list is when list is
[] -> [] ->
Ok(state) Ok(state)

View file

@ -618,7 +618,7 @@ is_gte : Num a, Num a -> Bool
## If either argument is [*NaN*](Num.is_nan), returns `Bool.false` no matter what. (*NaN* ## 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 [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 is_approx_eq : Frac a, Frac a, { rtol ?? Frac a, atol ?? Frac a } -> Bool
is_approx_eq = \x, y, { rtol ?? 0.00001, atol ?? 0.00000001 } -> is_approx_eq = |x, y, { rtol ?? 0.00001, atol ?? 0.00000001 }|
eq = x <= y && x >= y eq = x <= y && x >= y
meets_tolerance = Num.abs_diff(x, y) <= Num.max(atol, (rtol * Num.max(Num.abs(x), Num.abs(y)))) meets_tolerance = Num.abs_diff(x, y) <= Num.max(atol, (rtol * Num.max(Num.abs(x), Num.abs(y))))
eq || meets_tolerance eq || meets_tolerance
@ -630,21 +630,21 @@ is_zero : Num a -> Bool
## ##
## Examples of even numbers: 0, 2, 4, 6, 8, -2, -4, -6, -8 ## Examples of even numbers: 0, 2, 4, 6, 8, -2, -4, -6, -8
is_even : Int a -> Bool is_even : Int a -> Bool
is_even = \x -> Num.is_multiple_of(x, 2) is_even = |x| Num.is_multiple_of(x, 2)
## A number is odd if dividing it by 2 gives a remainder of 1. ## 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 ## Examples of odd numbers: 1, 3, 5, 7, -1, -3, -5, -7
is_odd : Int a -> Bool is_odd : Int a -> Bool
is_odd = \x -> Bool.not(Num.is_multiple_of(x, 2)) is_odd = |x| Bool.not(Num.is_multiple_of(x, 2))
## Positive numbers are greater than `0`. ## Positive numbers are greater than `0`.
is_positive : Num a -> Bool is_positive : Num a -> Bool
is_positive = \x -> x > 0 is_positive = |x| x > 0
## Negative numbers are less than `0`. ## Negative numbers are less than `0`.
is_negative : Num a -> Bool is_negative : Num a -> Bool
is_negative = \x -> x < 0 is_negative = |x| x < 0
to_frac : Num * -> Frac * to_frac : Num * -> Frac *
@ -709,7 +709,7 @@ abs : Num a -> Num a
## *overflow*. For [F64] and [F32], overflow results in an answer of either ## *overflow*. For [F64] and [F32], overflow results in an answer of either
## ∞ or -∞. For all other number types, overflow results in a panic. ## ∞ or -∞. For all other number types, overflow results in a panic.
abs_diff : Num a, Num a -> Num a abs_diff : Num a, Num a -> Num a
abs_diff = \a, b -> abs_diff = |a, b|
if a > b then if a > b then
a - b a - b
else else
@ -812,7 +812,7 @@ mul : Num a, Num a -> Num a
## Num.min(3.0, -3.0) ## Num.min(3.0, -3.0)
## ``` ## ```
min : Num a, Num a -> Num a min : Num a, Num a -> Num a
min = \a, b -> min = |a, b|
if a < b then if a < b then
a a
else else
@ -826,7 +826,7 @@ min = \a, b ->
## Num.max(3.0, -3.0) ## Num.max(3.0, -3.0)
## ``` ## ```
max : Num a, Num a -> Num a max : Num a, Num a -> Num a
max = \a, b -> max = |a, b|
if a > b then if a > b then
a a
else else
@ -868,7 +868,7 @@ atan : Frac a -> Frac a
sqrt : Frac a -> Frac a sqrt : Frac a -> Frac a
sqrt_checked : Frac a -> Result (Frac a) [SqrtOfNegative] sqrt_checked : Frac a -> Result (Frac a) [SqrtOfNegative]
sqrt_checked = \x -> sqrt_checked = |x|
if x < 0.0 then if x < 0.0 then
Err(SqrtOfNegative) Err(SqrtOfNegative)
else else
@ -878,7 +878,7 @@ sqrt_checked = \x ->
log : Frac a -> Frac a log : Frac a -> Frac a
log_checked : Frac a -> Result (Frac a) [LogNeedsPositive] log_checked : Frac a -> Result (Frac a) [LogNeedsPositive]
log_checked = \x -> log_checked = |x|
if x <= 0.0 then if x <= 0.0 then
Err(LogNeedsPositive) Err(LogNeedsPositive)
else else
@ -918,7 +918,7 @@ log_checked = \x ->
div : Frac a, Frac a -> Frac a div : Frac a, Frac a -> Frac a
div_checked : Frac a, Frac a -> Result (Frac a) [DivByZero] div_checked : Frac a, Frac a -> Result (Frac a) [DivByZero]
div_checked = \a, b -> div_checked = |a, b|
if Num.is_zero(b) then if Num.is_zero(b) then
Err(DivByZero) Err(DivByZero)
else else
@ -927,7 +927,7 @@ div_checked = \a, b ->
div_ceil : Int a, Int a -> Int a div_ceil : Int a, Int a -> Int a
div_ceil_checked : Int a, Int a -> Result (Int a) [DivByZero] div_ceil_checked : Int a, Int a -> Result (Int a) [DivByZero]
div_ceil_checked = \a, b -> div_ceil_checked = |a, b|
if Num.is_zero(b) then if Num.is_zero(b) then
Err(DivByZero) Err(DivByZero)
else else
@ -950,14 +950,14 @@ div_ceil_checked = \a, b ->
## Num.div_trunc(8, -3) ## Num.div_trunc(8, -3)
## ``` ## ```
div_trunc : Int a, Int a -> Int a div_trunc : Int a, Int a -> Int a
div_trunc = \a, b -> div_trunc = |a, b|
if Num.is_zero(b) then if Num.is_zero(b) then
crash("Integer division by 0!") crash("Integer division by 0!")
else else
Num.div_trunc_unchecked(a, b) Num.div_trunc_unchecked(a, b)
div_trunc_checked : Int a, Int a -> Result (Int a) [DivByZero] div_trunc_checked : Int a, Int a -> Result (Int a) [DivByZero]
div_trunc_checked = \a, b -> div_trunc_checked = |a, b|
if Num.is_zero(b) then if Num.is_zero(b) then
Err(DivByZero) Err(DivByZero)
else else
@ -979,14 +979,14 @@ div_trunc_unchecked : Int a, Int a -> Int a
## Num.rem(-8, -3) ## Num.rem(-8, -3)
## ``` ## ```
rem : Int a, Int a -> Int a rem : Int a, Int a -> Int a
rem = \a, b -> rem = |a, b|
if Num.is_zero(b) then if Num.is_zero(b) then
crash("Integer division by 0!") crash("Integer division by 0!")
else else
Num.rem_unchecked(a, b) Num.rem_unchecked(a, b)
rem_checked : Int a, Int a -> Result (Int a) [DivByZero] rem_checked : Int a, Int a -> Result (Int a) [DivByZero]
rem_checked = \a, b -> rem_checked = |a, b|
if Num.is_zero(b) then if Num.is_zero(b) then
Err(DivByZero) Err(DivByZero)
else else
@ -1013,7 +1013,7 @@ bitwise_or : Int a, Int a -> Int a
## Returns the complement of x - the number you get by switching each 1 for a ## 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. ## 0 and each 0 for a 1. This is the same as -x - 1.
bitwise_not : Int a -> Int a bitwise_not : Int a -> Int a
bitwise_not = \n -> bitwise_not = |n|
bitwise_xor(n, sub_wrap(0, 1)) bitwise_xor(n, sub_wrap(0, 1))
## Bitwise left shift of a number by another ## Bitwise left shift of a number by another
@ -1134,7 +1134,7 @@ add_saturated : Num a, Num a -> Num a
## This is the same as [Num.add] except if the operation overflows, instead of ## This is the same as [Num.add] except if the operation overflows, instead of
## panicking or returning ∞ or -∞, it will return `Err Overflow`. ## panicking or returning ∞ or -∞, it will return `Err Overflow`.
add_checked : Num a, Num a -> Result (Num a) [Overflow] add_checked : Num a, Num a -> Result (Num a) [Overflow]
add_checked = \a, b -> add_checked = |a, b|
result = add_checked_lowlevel(a, b) result = add_checked_lowlevel(a, b)
if result.b then if result.b then
@ -1160,7 +1160,7 @@ sub_saturated : Num a, Num a -> Num a
## This is the same as [Num.sub] except if the operation overflows, instead of ## This is the same as [Num.sub] except if the operation overflows, instead of
## panicking or returning ∞ or -∞, it will return `Err Overflow`. ## panicking or returning ∞ or -∞, it will return `Err Overflow`.
sub_checked : Num a, Num a -> Result (Num a) [Overflow] sub_checked : Num a, Num a -> Result (Num a) [Overflow]
sub_checked = \a, b -> sub_checked = |a, b|
result = sub_checked_lowlevel(a, b) result = sub_checked_lowlevel(a, b)
if result.b then if result.b then
@ -1184,7 +1184,7 @@ mul_saturated : Num a, Num a -> Num a
## This is the same as [Num.mul] except if the operation overflows, instead of ## This is the same as [Num.mul] except if the operation overflows, instead of
## panicking or returning ∞ or -∞, it will return `Err Overflow`. ## panicking or returning ∞ or -∞, it will return `Err Overflow`.
mul_checked : Num a, Num a -> Result (Num a) [Overflow] mul_checked : Num a, Num a -> Result (Num a) [Overflow]
mul_checked = \a, b -> mul_checked = |a, b|
result = mul_checked_lowlevel(a, b) result = mul_checked_lowlevel(a, b)
if result.b then if result.b then
@ -1464,7 +1464,7 @@ f64_from_parts : { sign : Bool, exponent : U16, fraction : U64 } -> F64
## expect Num.from_bool(Bool.false) == 0 ## expect Num.from_bool(Bool.false) == 0
## ``` ## ```
from_bool : Bool -> Num * from_bool : Bool -> Num *
from_bool = \bool -> from_bool = |bool|
if bool then if bool then
1 1
else else

View file

@ -2,7 +2,7 @@ module [
Result, Result,
is_ok, is_ok,
is_err, is_err,
map, map_ok,
map_err, map_err,
map_both, map_both,
map2, map2,
@ -23,7 +23,7 @@ Result ok err : [Ok ok, Err err]
## Result.is_ok(Ok(5)) ## Result.is_ok(Ok(5))
## ``` ## ```
is_ok : Result ok err -> Bool is_ok : Result ok err -> Bool
is_ok = \result -> is_ok = |result|
when result is when result is
Ok(_) -> Bool.true Ok(_) -> Bool.true
Err(_) -> Bool.false Err(_) -> Bool.false
@ -33,7 +33,7 @@ is_ok = \result ->
## Result.is_err(Err("uh oh")) ## Result.is_err(Err("uh oh"))
## ``` ## ```
is_err : Result ok err -> Bool is_err : Result ok err -> Bool
is_err = \result -> is_err = |result|
when result is when result is
Ok(_) -> Bool.false Ok(_) -> Bool.false
Err(_) -> Bool.true Err(_) -> Bool.true
@ -45,7 +45,7 @@ is_err = \result ->
## Result.with_default(Err("uh oh"), 42) ## Result.with_default(Err("uh oh"), 42)
## ``` ## ```
with_default : Result ok err, ok -> ok with_default : Result ok err, ok -> ok
with_default = \result, default -> with_default = |result, default|
when result is when result is
Ok(value) -> value Ok(value) -> value
Err(_) -> default Err(_) -> default
@ -54,14 +54,14 @@ with_default = \result, default ->
## function on it. Then returns a new `Ok` holding the transformed value. If the ## 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`. ## result is `Err`, this has no effect. Use [map_err] to transform an `Err`.
## ```roc ## ```roc
## Result.map(Ok(12), Num.neg) ## Result.map_ok(Ok(12), Num.neg)
## Result.map(Err("yipes!"), Num.neg) ## Result.map_ok(Err("yipes!"), Num.neg)
## ``` ## ```
## ##
## Functions like `map` are common in Roc; see for example [List.map], ## Functions like `map` are common in Roc; see for example [List.map],
## `Set.map`, and `Dict.map`. ## `Set.map`, and `Dict.map`.
map : Result a err, (a -> b) -> Result b err map_ok : Result a err, (a -> b) -> Result b err
map = \result, transform -> map_ok = |result, transform|
when result is when result is
Ok(v) -> Ok(transform(v)) Ok(v) -> Ok(transform(v))
Err(e) -> Err(e) Err(e) -> Err(e)
@ -74,14 +74,14 @@ map = \result, transform ->
## Result.map_err(Ok(12), Str.is_empty) ## Result.map_err(Ok(12), Str.is_empty)
## ``` ## ```
map_err : Result ok a, (a -> b) -> Result ok b map_err : Result ok a, (a -> b) -> Result ok b
map_err = \result, transform -> map_err = |result, transform|
when result is when result is
Ok(v) -> Ok(v) Ok(v) -> Ok(v)
Err(e) -> Err(transform(e)) Err(e) -> Err(transform(e))
## Maps both the `Ok` and `Err` values of a `Result` to new values. ## Maps both the `Ok` and `Err` values of a `Result` to new values.
map_both : Result ok1 err1, (ok1 -> ok2), (err1 -> err2) -> Result ok2 err2 map_both : Result ok1 err1, (ok1 -> ok2), (err1 -> err2) -> Result ok2 err2
map_both = \result, ok_transform, err_transform -> map_both = |result, ok_transform, err_transform|
when result is when result is
Ok(val) -> Ok(ok_transform(val)) Ok(val) -> Ok(ok_transform(val))
Err(err) -> Err(err_transform(err)) Err(err) -> Err(err_transform(err))
@ -89,7 +89,7 @@ map_both = \result, ok_transform, err_transform ->
## Maps the `Ok` values of two `Result`s to a new value using a given transformation, ## Maps the `Ok` values of two `Result`s to a new value using a given transformation,
## or returns the first `Err` value encountered. ## or returns the first `Err` value encountered.
map2 : Result a err, Result b err, (a, b -> c) -> Result c err map2 : Result a err, Result b err, (a, b -> c) -> Result c err
map2 = \first_result, second_result, transform -> map2 = |first_result, second_result, transform|
when (first_result, second_result) is when (first_result, second_result) is
(Ok(first), Ok(second)) -> Ok(transform(first, second)) (Ok(first), Ok(second)) -> Ok(transform(first, second))
(Err(err), _) -> Err(err) (Err(err), _) -> Err(err)
@ -103,7 +103,7 @@ map2 = \first_result, second_result, transform ->
## Result.try(Err("yipes!"), (\num -> if num < 0 then Err("negative!") else Ok(-num))) ## Result.try(Err("yipes!"), (\num -> if num < 0 then Err("negative!") else Ok(-num)))
## ``` ## ```
try : Result a err, (a -> Result b err) -> Result b err try : Result a err, (a -> Result b err) -> Result b err
try = \result, transform -> try = |result, transform|
when result is when result is
Ok(v) -> transform(v) Ok(v) -> transform(v)
Err(e) -> Err(e) Err(e) -> Err(e)
@ -116,7 +116,7 @@ try = \result, transform ->
## Result.on_err(Err("42"), (\error_num -> Str.to_u64(error_num))) ## Result.on_err(Err("42"), (\error_num -> Str.to_u64(error_num)))
## ``` ## ```
on_err : Result a err, (err -> Result a other_err) -> Result a other_err on_err : Result a err, (err -> Result a other_err) -> Result a other_err
on_err = \result, transform -> on_err = |result, transform|
when result is when result is
Ok(v) -> Ok(v) Ok(v) -> Ok(v)
Err(e) -> transform(e) Err(e) -> transform(e)
@ -132,7 +132,7 @@ on_err = \result, transform ->
## ) ## )
## ``` ## ```
on_err! : Result a err, (err => Result a other_err) => Result a other_err on_err! : Result a err, (err => Result a other_err) => Result a other_err
on_err! = \result, transform! -> on_err! = |result, transform!|
when result is when result is
Ok(v) -> Ok(v) Ok(v) -> Ok(v)
Err(e) -> transform!(e) Err(e) -> transform!(e)

View file

@ -47,14 +47,14 @@ Set k := Dict.Dict k {} where k implements Hash & Eq
] ]
is_eq : Set k, Set k -> Bool is_eq : Set k, Set k -> Bool
is_eq = \xs, ys -> is_eq = |xs, ys|
if len(xs) != len(ys) then if len(xs) != len(ys) then
Bool.false Bool.false
else else
walk_until( walk_until(
xs, xs,
Bool.true, Bool.true,
\_, elem -> |_, elem|
if contains(ys, elem) then if contains(ys, elem) then
Continue(Bool.true) Continue(Bool.true)
else else
@ -62,12 +62,12 @@ is_eq = \xs, ys ->
) )
hash_set : hasher, Set k -> hasher where hasher implements Hasher hash_set : hasher, Set k -> hasher where hasher implements Hasher
hash_set = \hasher, @Set(inner) -> Hash.hash(hasher, inner) hash_set = |hasher, @Set(inner)| Hash.hash(hasher, inner)
to_inspector_set : Set k -> Inspector f where k implements Inspect & Hash & Eq, f implements InspectFormatter to_inspector_set : Set k -> Inspector f where k implements Inspect & Hash & Eq, f implements InspectFormatter
to_inspector_set = \set -> to_inspector_set = |set|
Inspect.custom( Inspect.custom(
\fmt -> |fmt|
Inspect.apply(Inspect.set(set, walk, Inspect.to_inspector), fmt), Inspect.apply(Inspect.set(set, walk, Inspect.to_inspector), fmt),
) )
@ -79,25 +79,25 @@ to_inspector_set = \set ->
## expect count_values == 0 ## expect count_values == 0
## ``` ## ```
empty : {} -> Set * empty : {} -> Set *
empty = \{} -> @Set(Dict.empty({})) empty = |{}| @Set(Dict.empty({}))
## Return a set with space allocated for a number of entries. This ## 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 ## may provide a performance optimization if you know how many entries will be
## inserted. ## inserted.
with_capacity : U64 -> Set * with_capacity : U64 -> Set *
with_capacity = \cap -> with_capacity = |cap|
@Set(Dict.with_capacity(cap)) @Set(Dict.with_capacity(cap))
## Enlarge the set for at least capacity additional elements ## Enlarge the set for at least capacity additional elements
reserve : Set k, U64 -> Set k reserve : Set k, U64 -> Set k
reserve = \@Set(dict), requested -> reserve = |@Set(dict), requested|
@Set(Dict.reserve(dict, requested)) @Set(Dict.reserve(dict, requested))
## Shrink the memory footprint of a set such that capacity is as small as possible. ## 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. ## 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. ## There will still be some overhead due to dictionary metadata always being a power of 2.
release_excess_capacity : Set k -> Set k release_excess_capacity : Set k -> Set k
release_excess_capacity = \@Set(dict) -> release_excess_capacity = |@Set(dict)|
@Set(Dict.release_excess_capacity(dict)) @Set(Dict.release_excess_capacity(dict))
## Creates a new `Set` with a single value. ## Creates a new `Set` with a single value.
@ -108,7 +108,7 @@ release_excess_capacity = \@Set(dict) ->
## expect count_values == 1 ## expect count_values == 1
## ``` ## ```
single : k -> Set k single : k -> Set k
single = \key -> single = |key|
Dict.single(key, {}) |> @Set Dict.single(key, {}) |> @Set
## Insert a value into a `Set`. ## Insert a value into a `Set`.
@ -124,7 +124,7 @@ single = \key ->
## expect count_values == 3 ## expect count_values == 3
## ``` ## ```
insert : Set k, k -> Set k insert : Set k, k -> Set k
insert = \@Set(dict), key -> insert = |@Set(dict), key|
Dict.insert(dict, key, {}) |> @Set Dict.insert(dict, key, {}) |> @Set
# Inserting a duplicate key has no effect. # Inserting a duplicate key has no effect.
@ -157,7 +157,7 @@ expect
## expect count_values == 3 ## expect count_values == 3
## ``` ## ```
len : Set * -> U64 len : Set * -> U64
len = \@Set(dict) -> len = |@Set(dict)|
Dict.len(dict) Dict.len(dict)
## Returns the max number of elements the set can hold before requiring a rehash. ## Returns the max number of elements the set can hold before requiring a rehash.
@ -169,7 +169,7 @@ len = \@Set(dict) ->
## capacity_of_set = Set.capacity(food_set) ## capacity_of_set = Set.capacity(food_set)
## ``` ## ```
capacity : Set * -> U64 capacity : Set * -> U64
capacity = \@Set(dict) -> capacity = |@Set(dict)|
Dict.capacity(dict) Dict.capacity(dict)
## Check if the set is empty. ## Check if the set is empty.
@ -179,7 +179,7 @@ capacity = \@Set(dict) ->
## Set.is_empty(Set.empty({})) ## Set.is_empty(Set.empty({}))
## ``` ## ```
is_empty : Set * -> Bool is_empty : Set * -> Bool
is_empty = \@Set(dict) -> is_empty = |@Set(dict)|
Dict.is_empty(dict) Dict.is_empty(dict)
# Inserting a duplicate key has no effect on length. # Inserting a duplicate key has no effect on length.
@ -209,7 +209,7 @@ expect
## expect has20 == Bool.true ## expect has20 == Bool.true
## ``` ## ```
remove : Set k, k -> Set k remove : Set k, k -> Set k
remove = \@Set(dict), key -> remove = |@Set(dict), key|
Dict.remove(dict, key) |> @Set Dict.remove(dict, key) |> @Set
## Test if a value is in the `Set`. ## Test if a value is in the `Set`.
@ -228,7 +228,7 @@ remove = \@Set(dict), key ->
## expect has_banana == Bool.false ## expect has_banana == Bool.false
## ``` ## ```
contains : Set k, k -> Bool contains : Set k, k -> Bool
contains = \@Set(dict), key -> contains = |@Set(dict), key|
Dict.contains(dict, key) Dict.contains(dict, key)
## Retrieve the values in a `Set` as a `List`. ## Retrieve the values in a `Set` as a `List`.
@ -241,7 +241,7 @@ contains = \@Set(dict), key ->
## expect Set.to_list(numbers) == values ## expect Set.to_list(numbers) == values
## ``` ## ```
to_list : Set k -> List k to_list : Set k -> List k
to_list = \@Set(dict) -> to_list = |@Set(dict)|
Dict.keys(dict) Dict.keys(dict)
## Create a `Set` from a `List` of values. ## Create a `Set` from a `List` of values.
@ -255,9 +255,9 @@ to_list = \@Set(dict) ->
## expect Set.from_list([Pear, Apple, Banana]) == values ## expect Set.from_list([Pear, Apple, Banana]) == values
## ``` ## ```
from_list : List k -> Set k from_list : List k -> Set k
from_list = \list -> from_list = |list|
list list
|> List.map(\k -> (k, {})) |> List.map(|k| (k, {}))
|> Dict.from_list |> Dict.from_list
|> @Set |> @Set
@ -272,7 +272,7 @@ from_list = \list ->
## expect Set.union(set1, set2) == Set.from_list([Left, Right]) ## expect Set.union(set1, set2) == Set.from_list([Left, Right])
## ``` ## ```
union : Set k, Set k -> Set k union : Set k, Set k -> Set k
union = \@Set(dict1), @Set(dict2) -> union = |@Set(dict1), @Set(dict2)|
Dict.insert_all(dict1, dict2) |> @Set Dict.insert_all(dict1, dict2) |> @Set
## Combine two `Set`s by keeping the [intersection](https://en.wikipedia.org/wiki/Intersection_(set_theory)) ## Combine two `Set`s by keeping the [intersection](https://en.wikipedia.org/wiki/Intersection_(set_theory))
@ -285,7 +285,7 @@ union = \@Set(dict1), @Set(dict2) ->
## expect Set.intersection(set1, set2) == Set.single(Left) ## expect Set.intersection(set1, set2) == Set.single(Left)
## ``` ## ```
intersection : Set k, Set k -> Set k intersection : Set k, Set k -> Set k
intersection = \@Set(dict1), @Set(dict2) -> intersection = |@Set(dict1), @Set(dict2)|
Dict.keep_shared(dict1, dict2) |> @Set Dict.keep_shared(dict1, dict2) |> @Set
## Remove the values in the first `Set` that are also in the second `Set` ## Remove the values in the first `Set` that are also in the second `Set`
@ -299,7 +299,7 @@ intersection = \@Set(dict1), @Set(dict2) ->
## expect Set.difference(first, second) == Set.from_list([Up, Down]) ## expect Set.difference(first, second) == Set.from_list([Up, Down])
## ``` ## ```
difference : Set k, Set k -> Set k difference : Set k, Set k -> Set k
difference = \@Set(dict1), @Set(dict2) -> difference = |@Set(dict1), @Set(dict2)|
Dict.remove_all(dict1, dict2) |> @Set Dict.remove_all(dict1, dict2) |> @Set
## Iterate through the values of a given `Set` and build a value. ## Iterate through the values of a given `Set` and build a value.
@ -322,20 +322,20 @@ difference = \@Set(dict1), @Set(dict2) ->
## expect result == 2 ## expect result == 2
## ``` ## ```
walk : Set k, state, (state, k -> state) -> state walk : Set k, state, (state, k -> state) -> state
walk = \@Set(dict), state, step -> walk = |@Set(dict), state, step|
Dict.walk(dict, state, \s, k, _ -> step(s, k)) Dict.walk(dict, state, |s, k, _| step(s, k))
## Convert each value in the set to something new, by calling a conversion ## 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 ## function on each of them which receives the old value. Then return a
## new set containing the converted values. ## new set containing the converted values.
map : Set a, (a -> b) -> Set b map : Set a, (a -> b) -> Set b
map = \set, transform -> map = |set, transform|
init = with_capacity(capacity(set)) init = with_capacity(capacity(set))
walk( walk(
set, set,
init, init,
\answer, k -> |answer, k|
insert(answer, transform(k)), insert(answer, transform(k)),
) )
@ -345,13 +345,13 @@ map = \set, transform ->
## ##
## You may know a similar function named `concat_map` in other languages. ## You may know a similar function named `concat_map` in other languages.
join_map : Set a, (a -> Set b) -> Set b join_map : Set a, (a -> Set b) -> Set b
join_map = \set, transform -> join_map = |set, transform|
init = with_capacity(capacity(set)) # Might be a pessimization init = with_capacity(capacity(set)) # Might be a pessimization
walk( walk(
set, set,
init, init,
\answer, k -> |answer, k|
union(answer, transform(k)), union(answer, transform(k)),
) )
@ -371,8 +371,8 @@ join_map = \set, transform ->
## expect result == FoundTheAnswer ## expect result == FoundTheAnswer
## ``` ## ```
walk_until : Set k, state, (state, k -> [Continue state, Break state]) -> state walk_until : Set k, state, (state, k -> [Continue state, Break state]) -> state
walk_until = \@Set(dict), state, step -> walk_until = |@Set(dict), state, step|
Dict.walk_until(dict, state, \s, k, _ -> step(s, k)) Dict.walk_until(dict, state, |s, k, _| step(s, k))
## Run the given function on each element in the `Set`, and return ## 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`. ## a `Set` with just the elements for which the function returned `Bool.true`.
@ -382,8 +382,8 @@ walk_until = \@Set(dict), state, step ->
## |> Bool.is_eq(Set.from_list([3,4,5])) ## |> Bool.is_eq(Set.from_list([3,4,5]))
## ``` ## ```
keep_if : Set k, (k -> Bool) -> Set k keep_if : Set k, (k -> Bool) -> Set k
keep_if = \@Set(dict), predicate -> keep_if = |@Set(dict), predicate|
@Set(Dict.keep_if(dict, \(k, _v) -> predicate(k))) @Set(Dict.keep_if(dict, |(k, _v)| predicate(k)))
## Run the given function on each element in the `Set`, and return ## 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`. ## a `Set` with just the elements for which the function returned `Bool.false`.
@ -393,8 +393,8 @@ keep_if = \@Set(dict), predicate ->
## |> Bool.is_eq(Set.from_list([1,2])) ## |> Bool.is_eq(Set.from_list([1,2]))
## ``` ## ```
drop_if : Set k, (k -> Bool) -> Set k drop_if : Set k, (k -> Bool) -> Set k
drop_if = \@Set(dict), predicate -> drop_if = |@Set(dict), predicate|
@Set(Dict.drop_if(dict, \(k, _v) -> predicate(k))) @Set(Dict.drop_if(dict, |(k, _v)| predicate(k)))
expect expect
first = first =
@ -498,10 +498,10 @@ expect
expect expect
Set.from_list([1, 2, 3, 4, 5]) Set.from_list([1, 2, 3, 4, 5])
|> Set.keep_if(\k -> k >= 3) |> Set.keep_if(|k| k >= 3)
|> Bool.is_eq(Set.from_list([3, 4, 5])) |> Bool.is_eq(Set.from_list([3, 4, 5]))
expect expect
Set.from_list([1, 2, 3, 4, 5]) Set.from_list([1, 2, 3, 4, 5])
|> Set.drop_if(\k -> k >= 3) |> Set.drop_if(|k| k >= 3)
|> Bool.is_eq(Set.from_list([1, 2])) |> Bool.is_eq(Set.from_list([1, 2]))

View file

@ -541,7 +541,7 @@ to_utf8 : Str -> List U8
## expect Str.from_utf8([255]) |> Result.is_err ## expect Str.from_utf8([255]) |> Result.is_err
## ``` ## ```
from_utf8 : List U8 -> Result Str [BadUtf8 { problem : Utf8Problem, index : U64 }] from_utf8 : List U8 -> Result Str [BadUtf8 { problem : Utf8Problem, index : U64 }]
from_utf8 = \bytes -> from_utf8 = |bytes|
result = from_utf8_lowlevel bytes result = from_utf8_lowlevel bytes
if result.c_is_ok then if result.c_is_ok then
@ -598,11 +598,11 @@ expect (Str.from_utf8_lossy [82, 0xED, 0xA0, 0xBD, 99]) == "R<>c"
## expect Str.from_utf16([82, 0xdc96, 99]) |> Result.isErr ## expect Str.from_utf16([82, 0xdc96, 99]) |> Result.isErr
## ``` ## ```
from_utf16 : List U16 -> Result Str [BadUtf16 { problem : Utf8Problem, index : U64 }] from_utf16 : List U16 -> Result Str [BadUtf16 { problem : Utf8Problem, index : U64 }]
from_utf16 = \codeunits -> from_utf16 = |codeunits|
mk_err = \problem, index -> mk_err = |problem, index|
Err(BadUtf16({ problem, index })) Err(BadUtf16({ problem, index }))
step = \state, unit -> step = |state, unit|
c : U32 c : U32
c = Num.int_cast(unit) c = Num.int_cast(unit)
when state is when state is
@ -630,7 +630,7 @@ from_utf16 = \codeunits ->
when decode_res is when decode_res is
ExpectFirst(_, utf8) -> ExpectFirst(_, utf8) ->
from_utf8(utf8) from_utf8(utf8)
|> Result.map_err(\BadUtf8(err) -> BadUtf16(err)) |> Result.map_err(|BadUtf8(err)| BadUtf16(err))
ExpectSecond(i, _, _) -> ExpectSecond(i, _, _) ->
mk_err(EncodesSurrogateHalf, i) mk_err(EncodesSurrogateHalf, i)
@ -653,14 +653,14 @@ expect Str.from_utf16([82, 0xdc96, 99]) == Err(BadUtf16({ index: 1, problem: Enc
## expect Str.from_utf16_lossy([82, 0xdc96, 99]) == "R<>c" ## expect Str.from_utf16_lossy([82, 0xdc96, 99]) == "R<>c"
## ``` ## ```
from_utf16_lossy : List U16 -> Str from_utf16_lossy : List U16 -> Str
from_utf16_lossy = \codeunits -> from_utf16_lossy = |codeunits|
utf8_replacement = [0xef, 0xbf, 0xbd] utf8_replacement = [0xef, 0xbf, 0xbd]
encode_lossy = \utf8, c -> encode_lossy = |utf8, c|
when encode_utf8(utf8, c) is when encode_utf8(utf8, c) is
Ok(utf8_next) -> utf8_next Ok(utf8_next) -> utf8_next
Err(_) -> List.concat(utf8, utf8_replacement) Err(_) -> List.concat(utf8, utf8_replacement)
step = \state, unit -> step = |state, unit|
c : U32 c : U32
c = Num.int_cast(unit) c = Num.int_cast(unit)
when state is when state is
@ -704,8 +704,8 @@ expect Str.from_utf16_lossy([82, 0xdc96, 99]) == "R<>c"
## ``` ## ```
from_utf32 : List U32 -> Result Str [BadUtf32 { problem : Utf8Problem, index : U64 }] from_utf32 : List U32 -> Result Str [BadUtf32 { problem : Utf8Problem, index : U64 }]
from_utf32 = \codepoints -> from_utf32 = |codepoints|
step = \state, c -> step = |state, c|
when state is when state is
Ok({ i, utf8 }) -> Ok({ i, utf8 }) ->
when encode_utf8(utf8, c) is when encode_utf8(utf8, c) is
@ -716,14 +716,14 @@ from_utf32 = \codepoints ->
List.walk(codepoints, Ok({ i: 0, utf8: [] }), step) List.walk(codepoints, Ok({ i: 0, utf8: [] }), step)
|> Result.try( |> Result.try(
\state -> |state|
when from_utf8(state.utf8) is when from_utf8(state.utf8) is
Ok(str) -> Ok(str) Ok(str) -> Ok(str)
Err(BadUtf8(err)) -> Err(BadUtf32(err)), Err(BadUtf8(err)) -> Err(BadUtf32(err)),
) )
encode_utf8 : List U8, U32 -> Result (List U8) [EncodesSurrogateHalf, CodepointTooLarge] encode_utf8 : List U8, U32 -> Result (List U8) [EncodesSurrogateHalf, CodepointTooLarge]
encode_utf8 = \list, c -> encode_utf8 = |list, c|
if c < 0x80 then if c < 0x80 then
Ok(List.append(list, Num.int_cast(c))) Ok(List.append(list, Num.int_cast(c)))
else if c < 0x800 then else if c < 0x800 then
@ -782,8 +782,8 @@ expect Str.from_utf32([82, 0x110000, 99]) |> Result.is_err
## expect Str.from_utf32_lossy([82, 0x110000, 99]) == "R<>c" ## expect Str.from_utf32_lossy([82, 0x110000, 99]) == "R<>c"
## ``` ## ```
from_utf32_lossy : List U32 -> Str from_utf32_lossy : List U32 -> Str
from_utf32_lossy = \codepoints -> from_utf32_lossy = |codepoints|
step = \utf8, c -> step = |utf8, c|
when encode_utf8(utf8, c) is when encode_utf8(utf8, c) is
Ok(utf8_next) -> utf8_next Ok(utf8_next) -> utf8_next
# utf-8 encoded replacement character # utf-8 encoded replacement character
@ -836,7 +836,7 @@ trim_end : Str -> Str
## expect Str.to_dec("not a number") == Err(InvalidNumStr) ## expect Str.to_dec("not a number") == Err(InvalidNumStr)
## ``` ## ```
to_dec : Str -> Result Dec [InvalidNumStr] to_dec : Str -> Result Dec [InvalidNumStr]
to_dec = \string -> str_to_num_help(string) to_dec = |string| str_to_num_help(string)
## Encode a [Str] to a [F64]. A [F64] value is a 64-bit ## 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 ## [floating-point number](https://en.wikipedia.org/wiki/IEEE_754) and can be
@ -846,7 +846,7 @@ to_dec = \string -> str_to_num_help(string)
## expect Str.to_f64("not a number") == Err(InvalidNumStr) ## expect Str.to_f64("not a number") == Err(InvalidNumStr)
## ``` ## ```
to_f64 : Str -> Result F64 [InvalidNumStr] to_f64 : Str -> Result F64 [InvalidNumStr]
to_f64 = \string -> str_to_num_help(string) to_f64 = |string| str_to_num_help(string)
## Encode a [Str] to a [F32].A [F32] value is a 32-bit ## 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 ## [floating-point number](https://en.wikipedia.org/wiki/IEEE_754) and can be
@ -856,7 +856,7 @@ to_f64 = \string -> str_to_num_help(string)
## expect Str.to_f32("not a number") == Err(InvalidNumStr) ## expect Str.to_f32("not a number") == Err(InvalidNumStr)
## ``` ## ```
to_f32 : Str -> Result F32 [InvalidNumStr] to_f32 : Str -> Result F32 [InvalidNumStr]
to_f32 = \string -> str_to_num_help(string) to_f32 = |string| str_to_num_help(string)
## Encode a [Str] to an unsigned [U128] integer. A [U128] value can hold numbers ## 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 ## from `0` to `340_282_366_920_938_463_463_374_607_431_768_211_455` (over
@ -868,7 +868,7 @@ to_f32 = \string -> str_to_num_help(string)
## expect Str.to_u128("not a number") == Err(InvalidNumStr) ## expect Str.to_u128("not a number") == Err(InvalidNumStr)
## ``` ## ```
to_u128 : Str -> Result U128 [InvalidNumStr] to_u128 : Str -> Result U128 [InvalidNumStr]
to_u128 = \string -> str_to_num_help(string) to_u128 = |string| str_to_num_help(string)
## Encode a [Str] to a signed [I128] integer. A [I128] value can hold numbers ## 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 ## from `-170_141_183_460_469_231_731_687_303_715_884_105_728` to
@ -881,7 +881,7 @@ to_u128 = \string -> str_to_num_help(string)
## expect Str.to_i128("not a number") == Err(InvalidNumStr) ## expect Str.to_i128("not a number") == Err(InvalidNumStr)
## ``` ## ```
to_i128 : Str -> Result I128 [InvalidNumStr] to_i128 : Str -> Result I128 [InvalidNumStr]
to_i128 = \string -> str_to_num_help(string) to_i128 = |string| str_to_num_help(string)
## Encode a [Str] to an unsigned [U64] integer. A [U64] value can hold numbers ## 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 ## from `0` to `18_446_744_073_709_551_615` (over 18 quintillion). It
@ -893,7 +893,7 @@ to_i128 = \string -> str_to_num_help(string)
## expect Str.to_u64("not a number") == Err(InvalidNumStr) ## expect Str.to_u64("not a number") == Err(InvalidNumStr)
## ``` ## ```
to_u64 : Str -> Result U64 [InvalidNumStr] to_u64 : Str -> Result U64 [InvalidNumStr]
to_u64 = \string -> str_to_num_help(string) to_u64 = |string| str_to_num_help(string)
## Encode a [Str] to a signed [I64] integer. A [I64] value can hold numbers ## 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 ## from `-9_223_372_036_854_775_808` to `9_223_372_036_854_775_807`. It can be
@ -905,7 +905,7 @@ to_u64 = \string -> str_to_num_help(string)
## expect Str.to_i64("not a number") == Err(InvalidNumStr) ## expect Str.to_i64("not a number") == Err(InvalidNumStr)
## ``` ## ```
to_i64 : Str -> Result I64 [InvalidNumStr] to_i64 : Str -> Result I64 [InvalidNumStr]
to_i64 = \string -> str_to_num_help(string) to_i64 = |string| str_to_num_help(string)
## Encode a [Str] to an unsigned [U32] integer. A [U32] value can hold numbers ## 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 ## from `0` to `4_294_967_295` (over 4 billion). It can be specified with
@ -917,7 +917,7 @@ to_i64 = \string -> str_to_num_help(string)
## expect Str.to_u32("not a number") == Err(InvalidNumStr) ## expect Str.to_u32("not a number") == Err(InvalidNumStr)
## ``` ## ```
to_u32 : Str -> Result U32 [InvalidNumStr] to_u32 : Str -> Result U32 [InvalidNumStr]
to_u32 = \string -> str_to_num_help(string) to_u32 = |string| str_to_num_help(string)
## Encode a [Str] to a signed [I32] integer. A [I32] value can hold numbers ## 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 ## from `-2_147_483_648` to `2_147_483_647`. It can be
@ -929,7 +929,7 @@ to_u32 = \string -> str_to_num_help(string)
## expect Str.to_i32("not a number") == Err(InvalidNumStr) ## expect Str.to_i32("not a number") == Err(InvalidNumStr)
## ``` ## ```
to_i32 : Str -> Result I32 [InvalidNumStr] to_i32 : Str -> Result I32 [InvalidNumStr]
to_i32 = \string -> str_to_num_help(string) to_i32 = |string| str_to_num_help(string)
## Encode a [Str] to an unsigned [U16] integer. A [U16] value can hold numbers ## 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. ## from `0` to `65_535`. It can be specified with a u16 suffix.
@ -940,7 +940,7 @@ to_i32 = \string -> str_to_num_help(string)
## expect Str.to_u16("not a number") == Err(InvalidNumStr) ## expect Str.to_u16("not a number") == Err(InvalidNumStr)
## ``` ## ```
to_u16 : Str -> Result U16 [InvalidNumStr] to_u16 : Str -> Result U16 [InvalidNumStr]
to_u16 = \string -> str_to_num_help(string) to_u16 = |string| str_to_num_help(string)
## Encode a [Str] to a signed [I16] integer. A [I16] value can hold numbers ## Encode a [Str] to a signed [I16] integer. A [I16] value can hold numbers
## from `-32_768` to `32_767`. It can be ## from `-32_768` to `32_767`. It can be
@ -952,7 +952,7 @@ to_u16 = \string -> str_to_num_help(string)
## expect Str.to_i16("not a number") == Err(InvalidNumStr) ## expect Str.to_i16("not a number") == Err(InvalidNumStr)
## ``` ## ```
to_i16 : Str -> Result I16 [InvalidNumStr] to_i16 : Str -> Result I16 [InvalidNumStr]
to_i16 = \string -> str_to_num_help(string) to_i16 = |string| str_to_num_help(string)
## Encode a [Str] to an unsigned [U8] integer. A [U8] value can hold numbers ## 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. ## from `0` to `255`. It can be specified with a u8 suffix.
@ -963,7 +963,7 @@ to_i16 = \string -> str_to_num_help(string)
## expect Str.to_u8("1500") == Err(InvalidNumStr) ## expect Str.to_u8("1500") == Err(InvalidNumStr)
## ``` ## ```
to_u8 : Str -> Result U8 [InvalidNumStr] to_u8 : Str -> Result U8 [InvalidNumStr]
to_u8 = \string -> str_to_num_help(string) to_u8 = |string| str_to_num_help(string)
## Encode a [Str] to a signed [I8] integer. A [I8] value can hold numbers ## Encode a [Str] to a signed [I8] integer. A [I8] value can hold numbers
## from `-128` to `127`. It can be ## from `-128` to `127`. It can be
@ -974,7 +974,7 @@ to_u8 = \string -> str_to_num_help(string)
## expect Str.to_i8("not a number") == Err(InvalidNumStr) ## expect Str.to_i8("not a number") == Err(InvalidNumStr)
## ``` ## ```
to_i8 : Str -> Result I8 [InvalidNumStr] to_i8 : Str -> Result I8 [InvalidNumStr]
to_i8 = \string -> str_to_num_help(string) to_i8 = |string| str_to_num_help(string)
## Get the byte at the given index, without performing a bounds check. ## Get the byte at the given index, without performing a bounds check.
get_unsafe : Str, U64 -> U8 get_unsafe : Str, U64 -> U8
@ -996,7 +996,7 @@ substring_unsafe : Str, U64, U64 -> Str
## expect Str.replace_each("not here", "/", "_") == "not here" ## expect Str.replace_each("not here", "/", "_") == "not here"
## ``` ## ```
replace_each : Str, Str, Str -> Str replace_each : Str, Str, Str -> Str
replace_each = \haystack, needle, flower -> replace_each = |haystack, needle, flower|
when split_first(haystack, needle) is when split_first(haystack, needle) is
Ok({ before, after }) -> Ok({ before, after }) ->
# We found at least one needle, so start the buffer off with # We found at least one needle, so start the buffer off with
@ -1009,7 +1009,7 @@ replace_each = \haystack, needle, flower ->
Err(NotFound) -> haystack Err(NotFound) -> haystack
replace_each_help : Str, Str, Str, Str -> Str replace_each_help : Str, Str, Str, Str -> Str
replace_each_help = \buf, haystack, needle, flower -> replace_each_help = |buf, haystack, needle, flower|
when split_first(haystack, needle) is when split_first(haystack, needle) is
Ok({ before, after }) -> Ok({ before, after }) ->
buf buf
@ -1030,7 +1030,7 @@ expect Str.replace_each("abcdefg", "nothing", "_") == "abcdefg"
## expect Str.replace_first("no slashes here", "/", "_") == "no slashes here" ## expect Str.replace_first("no slashes here", "/", "_") == "no slashes here"
## ``` ## ```
replace_first : Str, Str, Str -> Str replace_first : Str, Str, Str -> Str
replace_first = \haystack, needle, flower -> replace_first = |haystack, needle, flower|
when split_first(haystack, needle) is when split_first(haystack, needle) is
Ok({ before, after }) -> Ok({ before, after }) ->
"${before}${flower}${after}" "${before}${flower}${after}"
@ -1048,7 +1048,7 @@ expect Str.replace_first("abcdefg", "nothing", "_") == "abcdefg"
## expect Str.replace_last("no slashes here", "/", "_") == "no slashes here" ## expect Str.replace_last("no slashes here", "/", "_") == "no slashes here"
## ``` ## ```
replace_last : Str, Str, Str -> Str replace_last : Str, Str, Str -> Str
replace_last = \haystack, needle, flower -> replace_last = |haystack, needle, flower|
when split_last(haystack, needle) is when split_last(haystack, needle) is
Ok({ before, after }) -> Ok({ before, after }) ->
"${before}${flower}${after}" "${before}${flower}${after}"
@ -1066,7 +1066,7 @@ expect Str.replace_last("abcdefg", "nothing", "_") == "abcdefg"
## expect Str.split_first("no slashes here", "/") == Err(NotFound) ## expect Str.split_first("no slashes here", "/") == Err(NotFound)
## ``` ## ```
split_first : Str, Str -> Result { before : Str, after : Str } [NotFound] split_first : Str, Str -> Result { before : Str, after : Str } [NotFound]
split_first = \haystack, needle -> split_first = |haystack, needle|
when first_match(haystack, needle) is when first_match(haystack, needle) is
Some(index) -> Some(index) ->
remaining = Str.count_utf8_bytes(haystack) - Str.count_utf8_bytes(needle) - index remaining = Str.count_utf8_bytes(haystack) - Str.count_utf8_bytes(needle) - index
@ -1095,7 +1095,7 @@ expect split_first("hullabaloo", "ab") == Ok({ before: "hull", after: "aloo" })
expect split_first("foo", "foo") == Ok({ before: "", after: "" }) expect split_first("foo", "foo") == Ok({ before: "", after: "" })
first_match : Str, Str -> [Some U64, None] first_match : Str, Str -> [Some U64, None]
first_match = \haystack, needle -> first_match = |haystack, needle|
haystack_length = Str.count_utf8_bytes(haystack) haystack_length = Str.count_utf8_bytes(haystack)
needle_length = Str.count_utf8_bytes(needle) needle_length = Str.count_utf8_bytes(needle)
last_possible = Num.sub_saturated(haystack_length, needle_length) last_possible = Num.sub_saturated(haystack_length, needle_length)
@ -1103,7 +1103,7 @@ first_match = \haystack, needle ->
first_match_help(haystack, needle, 0, last_possible) first_match_help(haystack, needle, 0, last_possible)
first_match_help : Str, Str, U64, U64 -> [Some U64, None] first_match_help : Str, Str, U64, U64 -> [Some U64, None]
first_match_help = \haystack, needle, index, last_possible -> first_match_help = |haystack, needle, index, last_possible|
if index <= last_possible then if index <= last_possible then
if matches_at(haystack, index, needle) then if matches_at(haystack, index, needle) then
Some(index) Some(index)
@ -1120,7 +1120,7 @@ first_match_help = \haystack, needle, index, last_possible ->
## expect Str.split_last("no slashes here", "/") == Err(NotFound) ## expect Str.split_last("no slashes here", "/") == Err(NotFound)
## ``` ## ```
split_last : Str, Str -> Result { before : Str, after : Str } [NotFound] split_last : Str, Str -> Result { before : Str, after : Str } [NotFound]
split_last = \haystack, needle -> split_last = |haystack, needle|
when last_match(haystack, needle) is when last_match(haystack, needle) is
Some(index) -> Some(index) ->
remaining = Str.count_utf8_bytes(haystack) - Str.count_utf8_bytes(needle) - index remaining = Str.count_utf8_bytes(haystack) - Str.count_utf8_bytes(needle) - index
@ -1146,7 +1146,7 @@ expect Str.split_last("hullabaloo", "ab") == Ok({ before: "hull", after: "aloo"
expect Str.split_last("foo", "foo") == Ok({ before: "", after: "" }) expect Str.split_last("foo", "foo") == Ok({ before: "", after: "" })
last_match : Str, Str -> [Some U64, None] last_match : Str, Str -> [Some U64, None]
last_match = \haystack, needle -> last_match = |haystack, needle|
haystack_length = Str.count_utf8_bytes(haystack) haystack_length = Str.count_utf8_bytes(haystack)
needle_length = Str.count_utf8_bytes(needle) needle_length = Str.count_utf8_bytes(needle)
last_possible_index = Num.sub_saturated(haystack_length, needle_length) last_possible_index = Num.sub_saturated(haystack_length, needle_length)
@ -1154,7 +1154,7 @@ last_match = \haystack, needle ->
last_match_help(haystack, needle, last_possible_index) last_match_help(haystack, needle, last_possible_index)
last_match_help : Str, Str, U64 -> [Some U64, None] last_match_help : Str, Str, U64 -> [Some U64, None]
last_match_help = \haystack, needle, index -> last_match_help = |haystack, needle, index|
if matches_at(haystack, index, needle) then if matches_at(haystack, index, needle) then
Some(index) Some(index)
else else
@ -1165,10 +1165,10 @@ last_match_help = \haystack, needle, index ->
Err(_) -> Err(_) ->
None None
min = \x, y -> if x < y then x else y min = |x, y| if x < y then x else y
matches_at : Str, U64, Str -> Bool matches_at : Str, U64, Str -> Bool
matches_at = \haystack, haystack_index, needle -> matches_at = |haystack, haystack_index, needle|
haystack_length = Str.count_utf8_bytes(haystack) haystack_length = Str.count_utf8_bytes(haystack)
needle_length = Str.count_utf8_bytes(needle) needle_length = Str.count_utf8_bytes(needle)
end_index = min(Num.add_saturated(haystack_index, needle_length), haystack_length) end_index = min(Num.add_saturated(haystack_index, needle_length), haystack_length)
@ -1184,7 +1184,7 @@ matches_at = \haystack, haystack_index, needle ->
}, },
) )
matches_at_help = \state -> matches_at_help = |state|
{ haystack, haystack_index, needle, needle_index, needle_length, end_index } = state { haystack, haystack_index, needle, needle_index, needle_length, end_index } = state
is_at_end_of_haystack = haystack_index >= end_index is_at_end_of_haystack = haystack_index >= end_index
@ -1216,11 +1216,11 @@ matches_at_help = \state ->
## expect Str.walk_utf8_with_index("ABC", [], f) == [65, 66, 67] ## expect Str.walk_utf8_with_index("ABC", [], f) == [65, 66, 67]
## ``` ## ```
walk_utf8_with_index : Str, state, (state, U8, U64 -> state) -> state walk_utf8_with_index : Str, state, (state, U8, U64 -> state) -> state
walk_utf8_with_index = \string, state, step -> walk_utf8_with_index = |string, state, step|
walk_utf8_with_index_help(string, state, step, 0, Str.count_utf8_bytes(string)) walk_utf8_with_index_help(string, state, step, 0, Str.count_utf8_bytes(string))
walk_utf8_with_index_help : Str, state, (state, U8, U64 -> state), U64, U64 -> state walk_utf8_with_index_help : Str, state, (state, U8, U64 -> state), U64, U64 -> state
walk_utf8_with_index_help = \string, state, step, index, length -> walk_utf8_with_index_help = |string, state, step, index, length|
if index < length then if index < length then
byte = Str.get_unsafe(string, index) byte = Str.get_unsafe(string, index)
new_state = step(state, byte, index) new_state = step(state, byte, index)
@ -1241,11 +1241,11 @@ walk_utf8_with_index_help = \string, state, step, index, length ->
## expect sum_of_utf8_bytes == 105 ## expect sum_of_utf8_bytes == 105
## ``` ## ```
walk_utf8 : Str, state, (state, U8 -> state) -> state walk_utf8 : Str, state, (state, U8 -> state) -> state
walk_utf8 = \str, initial, step -> walk_utf8 = |str, initial, step|
walk_utf8_help(str, initial, step, 0, Str.count_utf8_bytes(str)) walk_utf8_help(str, initial, step, 0, Str.count_utf8_bytes(str))
walk_utf8_help : Str, state, (state, U8 -> state), U64, U64 -> state walk_utf8_help : Str, state, (state, U8 -> state), U64, U64 -> state
walk_utf8_help = \str, state, step, index, length -> walk_utf8_help = |str, state, step, index, length|
if index < length then if index < length then
byte = Str.get_unsafe(str, index) byte = Str.get_unsafe(str, index)
new_state = step(state, byte) new_state = step(state, byte)
@ -1264,7 +1264,7 @@ release_excess_capacity : Str -> Str
str_to_num : Str -> { berrorcode : U8, aresult : Num * } str_to_num : Str -> { berrorcode : U8, aresult : Num * }
str_to_num_help : Str -> Result (Num a) [InvalidNumStr] str_to_num_help : Str -> Result (Num a) [InvalidNumStr]
str_to_num_help = \string -> str_to_num_help = |string|
result : { berrorcode : U8, aresult : Num a } result : { berrorcode : U8, aresult : Num a }
result = str_to_num(string) result = str_to_num(string)
@ -1278,7 +1278,7 @@ str_to_num_help = \string ->
## expect Str.with_prefix("Awesome", "Roc") == "RocAwesome" ## expect Str.with_prefix("Awesome", "Roc") == "RocAwesome"
## ``` ## ```
with_prefix : Str, Str -> Str with_prefix : Str, Str -> Str
with_prefix = \str, prefix -> Str.concat(prefix, str) with_prefix = |str, prefix| Str.concat(prefix, str)
## Determines whether or not the first Str contains the second. ## Determines whether or not the first Str contains the second.
## ```roc ## ```roc
@ -1287,7 +1287,7 @@ with_prefix = \str, prefix -> Str.concat(prefix, str)
## expect Str.contains("anything", "") ## expect Str.contains("anything", "")
## ``` ## ```
contains : Str, Str -> Bool contains : Str, Str -> Bool
contains = \haystack, needle -> contains = |haystack, needle|
when first_match(haystack, needle) is when first_match(haystack, needle) is
Some(_index) -> Bool.true Some(_index) -> Bool.true
None -> Bool.false None -> Bool.false
@ -1300,7 +1300,7 @@ contains = \haystack, needle ->
## expect Str.drop_prefix("foobar", "foo") == "bar" ## expect Str.drop_prefix("foobar", "foo") == "bar"
## ``` ## ```
drop_prefix : Str, Str -> Str drop_prefix : Str, Str -> Str
drop_prefix = \haystack, prefix -> drop_prefix = |haystack, prefix|
if Str.starts_with(haystack, prefix) then if Str.starts_with(haystack, prefix) then
start = Str.count_utf8_bytes(prefix) start = Str.count_utf8_bytes(prefix)
len = Num.sub_wrap(Str.count_utf8_bytes(haystack), start) len = Num.sub_wrap(Str.count_utf8_bytes(haystack), start)
@ -1317,7 +1317,7 @@ drop_prefix = \haystack, prefix ->
## expect Str.drop_suffix("barfoo", "foo") == "bar" ## expect Str.drop_suffix("barfoo", "foo") == "bar"
## ``` ## ```
drop_suffix : Str, Str -> Str drop_suffix : Str, Str -> Str
drop_suffix = \haystack, suffix -> drop_suffix = |haystack, suffix|
if Str.ends_with(haystack, suffix) then if Str.ends_with(haystack, suffix) then
start = 0 start = 0
len = Num.sub_wrap(Str.count_utf8_bytes(haystack), Str.count_utf8_bytes(suffix)) len = Num.sub_wrap(Str.count_utf8_bytes(haystack), Str.count_utf8_bytes(suffix))

View file

@ -5,7 +5,7 @@ use roc_collections::{ImMap, MutSet, SendMap, VecMap, VecSet};
use roc_module::ident::{Ident, IdentSuffix, Lowercase, TagName}; use roc_module::ident::{Ident, IdentSuffix, Lowercase, TagName};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_parse::ast::{ use roc_parse::ast::{
AssignedField, ExtractSpaces, FunctionArrow, Pattern, Tag, TypeAnnotation, TypeHeader, AssignedField, ExtractSpaces, FunctionArrow, Tag, TypeAnnotation, TypeHeader, TypeVar,
}; };
use roc_problem::can::{Problem, ShadowKind}; use roc_problem::can::{Problem, ShadowKind};
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
@ -754,9 +754,7 @@ fn can_annotation_help(
for loc_var in *loc_vars { for loc_var in *loc_vars {
let var = match loc_var.value { let var = match loc_var.value {
Pattern::Identifier { ident: name, .. } TypeVar::Identifier(name) if name.chars().next().unwrap().is_lowercase() => {
if name.chars().next().unwrap().is_lowercase() =>
{
name name
} }
_ => unreachable!("I thought this was validated during parsing"), _ => unreachable!("I thought this was validated during parsing"),

View file

@ -2858,7 +2858,7 @@ fn to_pending_alias_or_opaque<'a>(
env: &mut Env<'a>, env: &mut Env<'a>,
scope: &mut Scope, scope: &mut Scope,
name: &'a Loc<&'a str>, name: &'a Loc<&'a str>,
vars: &'a [Loc<ast::Pattern<'a>>], vars: &'a [Loc<ast::TypeVar<'a>>],
ann: &'a Loc<ast::TypeAnnotation<'a>>, ann: &'a Loc<ast::TypeAnnotation<'a>>,
opt_derived: Option<&'a ast::ImplementsAbilities<'a>>, opt_derived: Option<&'a ast::ImplementsAbilities<'a>>,
kind: AliasKind, kind: AliasKind,
@ -2870,10 +2870,9 @@ fn to_pending_alias_or_opaque<'a>(
let mut can_rigids: Vec<Loc<Lowercase>> = Vec::with_capacity(vars.len()); let mut can_rigids: Vec<Loc<Lowercase>> = Vec::with_capacity(vars.len());
for loc_var in vars.iter() { for loc_var in vars.iter() {
match loc_var.value { match loc_var.value.extract_spaces().item {
ast::Pattern::Identifier { ident: name, .. } ast::TypeVar::Identifier(name) => {
if name.chars().next().unwrap().is_lowercase() => debug_assert!(name.chars().next().unwrap().is_lowercase());
{
let lowercase = Lowercase::from(name); let lowercase = Lowercase::from(name);
can_rigids.push(Loc { can_rigids.push(Loc {
value: lowercase, value: lowercase,
@ -2881,7 +2880,6 @@ fn to_pending_alias_or_opaque<'a>(
}); });
} }
_ => { _ => {
// any other pattern in this position is a syntax error.
let problem = Problem::InvalidAliasRigid { let problem = Problem::InvalidAliasRigid {
alias_name: symbol, alias_name: symbol,
region: loc_var.region, region: loc_var.region,

View file

@ -4,7 +4,7 @@ use crate::env::Env;
use crate::scope::Scope; use crate::scope::Scope;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
use roc_module::called_via::BinOp::{DoubleQuestion, Pizza}; use roc_module::called_via::BinOp::{DoubleQuestion, Pizza, SingleQuestion};
use roc_module::called_via::{BinOp, CalledVia}; use roc_module::called_via::{BinOp, CalledVia};
use roc_module::ident::ModuleName; use roc_module::ident::ModuleName;
use roc_parse::ast::Expr::{self, *}; use roc_parse::ast::Expr::{self, *};
@ -213,62 +213,132 @@ fn new_op_call_expr<'a>(
let left = desugar_expr(env, scope, left); let left = desugar_expr(env, scope, left);
let right = desugar_expr(env, scope, right); let right = desugar_expr(env, scope, right);
let mut branches = Vec::with_capacity_in(2, env.arena); let ok_var = env.arena.alloc_str(&format!(
let mut branch_1_patts = Vec::with_capacity_in(1, env.arena); "#double_question_ok_{}_{}",
let mut branch_1_patts_args = Vec::with_capacity_in(1, env.arena);
let success_var = env.arena.alloc_str(
format!(
"success_BRANCH1_{}_{}",
left.region.start().offset, left.region.start().offset,
left.region.end().offset left.region.end().offset
)
.as_str(),
);
branch_1_patts_args.push(Loc::at(
left.region,
Pattern::Identifier { ident: success_var },
)); ));
let branch_1_tag: &Loc<Pattern<'a>> =
env.arena.alloc(Loc::at(left.region, Pattern::Tag("Ok"))); let ok_branch_pattern_args = env
branch_1_patts.push(Loc::at( .arena
.alloc([Loc::at(left.region, Pattern::Identifier { ident: ok_var })]);
let ok_branch_patterns = env.arena.alloc([Loc::at(
left.region, left.region,
Pattern::PncApply( Pattern::PncApply(
branch_1_tag, env.arena.alloc(Loc::at(left.region, Pattern::Tag("Ok"))),
Collection::with_items(branch_1_patts_args.into_bump_slice()), Collection::with_items(ok_branch_pattern_args),
), ),
)); )]);
let branch_one: &WhenBranch<'_> = env.arena.alloc(WhenBranch { let ok_branch = &*env.arena.alloc(WhenBranch {
patterns: branch_1_patts.into_bump_slice(), patterns: ok_branch_patterns,
value: Loc::at( value: Loc::at(
left.region, left.region,
Expr::Var { Expr::Var {
module_name: "", module_name: "",
ident: success_var, ident: ok_var,
}, },
), ),
guard: None, guard: None,
}); });
branches.push(branch_one);
let mut branch_2_patts = Vec::with_capacity_in(1, env.arena); let err_branch_pattern_args = env
let mut branch_2_patts_args = Vec::with_capacity_in(1, env.arena); .arena
branch_2_patts_args.push(Loc::at(right.region, Pattern::Underscore(""))); .alloc([(Loc::at(right.region, Pattern::Underscore("")))]);
let branch_2_tag: &Loc<Pattern<'a>> = let err_branch_patterns = env.arena.alloc([Loc::at(
env.arena.alloc(Loc::at(left.region, Pattern::Tag("Err")));
branch_2_patts.push(Loc::at(
right.region, right.region,
Pattern::PncApply( Pattern::PncApply(
branch_2_tag, env.arena.alloc(Loc::at(left.region, Pattern::Tag("Err"))),
Collection::with_items(branch_2_patts_args.into_bump_slice()), Collection::with_items(err_branch_pattern_args),
), ),
)); )]);
let branch_two: &WhenBranch<'_> = env.arena.alloc(WhenBranch { let err_branch = &*env.arena.alloc(WhenBranch {
patterns: branch_2_patts.into_bump_slice(), patterns: err_branch_patterns,
value: *right, value: *right,
guard: None, guard: None,
}); });
branches.push(branch_two);
When(left, branches.into_bump_slice()) When(left, &*env.arena.alloc([ok_branch, err_branch]))
}
SingleQuestion => {
let left = desugar_expr(env, scope, left);
let right = desugar_expr(env, scope, right);
let ok_var = env.arena.alloc_str(&format!(
"#single_question_ok_{}_{}",
left.region.start().offset,
left.region.end().offset
));
let ok_branch_pattern_args = env
.arena
.alloc([Loc::at(left.region, Pattern::Identifier { ident: ok_var })]);
let ok_branch_patterns = env.arena.alloc([Loc::at(
left.region,
Pattern::PncApply(
env.arena.alloc(Loc::at(left.region, Pattern::Tag("Ok"))),
Collection::with_items(ok_branch_pattern_args),
),
)]);
let ok_branch = &*env.arena.alloc(WhenBranch {
patterns: ok_branch_patterns,
value: Loc::at(
left.region,
Expr::Var {
module_name: "",
ident: ok_var,
},
),
guard: None,
});
let err_var = env.arena.alloc_str(&format!(
"#single_question_err_{}_{}",
left.region.start().offset,
left.region.end().offset
));
let err_branch_pattern_args = env
.arena
.alloc([(Loc::at(right.region, Pattern::Identifier { ident: err_var }))]);
let err_branch_patterns = env.arena.alloc([Loc::at(
right.region,
Pattern::PncApply(
env.arena.alloc(Loc::at(left.region, Pattern::Tag("Err"))),
Collection::with_items(err_branch_pattern_args),
),
)]);
let map_err_expr = &*env.arena.alloc(Loc::at(
right.region,
Expr::PncApply(
right,
Collection::with_items(&*env.arena.alloc([&*env.arena.alloc(Loc::at(
left.region,
Expr::Var {
module_name: "",
ident: err_var,
},
))])),
),
));
let err_branch = &*env.arena.alloc(WhenBranch {
patterns: err_branch_patterns,
value: Loc::at(
region,
Expr::Return(
env.arena.alloc(Loc::at(
region,
Expr::PncApply(
env.arena.alloc(Loc::at(region, Expr::Tag("Err"))),
Collection::with_items(&*env.arena.alloc([map_err_expr])),
),
)),
None,
),
),
guard: None,
});
When(left, &*env.arena.alloc([ok_branch, err_branch]))
} }
binop => { binop => {
let left = desugar_expr(env, scope, left); let left = desugar_expr(env, scope, left);
@ -1357,6 +1427,7 @@ fn desugar_pattern<'a>(env: &mut Env<'a>, scope: &mut Scope, pattern: Pattern<'a
| ListRest(_) | ListRest(_)
| Malformed(_) | Malformed(_)
| MalformedIdent(_, _) | MalformedIdent(_, _)
| MalformedExpr(_)
| QualifiedIdentifier { .. } => pattern, | QualifiedIdentifier { .. } => pattern,
Apply(tag, arg_patterns) => { Apply(tag, arg_patterns) => {
@ -1574,6 +1645,7 @@ fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) {
Or => (ModuleName::BOOL, "or"), Or => (ModuleName::BOOL, "or"),
Pizza => unreachable!("Cannot desugar the |> operator"), Pizza => unreachable!("Cannot desugar the |> operator"),
DoubleQuestion => unreachable!("Cannot desugar the ?? operator"), DoubleQuestion => unreachable!("Cannot desugar the ?? operator"),
SingleQuestion => unreachable!("Cannot desugar the ? operator"),
} }
} }

View file

@ -817,7 +817,7 @@ pub fn canonicalize_pattern<'a>(
} }
} }
Malformed(_str) => { Malformed(_) | MalformedExpr(_) => {
let problem = MalformedPatternProblem::Unknown; let problem = MalformedPatternProblem::Unknown;
malformed_pattern(env, problem, region) malformed_pattern(env, problem, region)
} }

View file

@ -284,7 +284,7 @@ mod test_can {
let src = indoc!( let src = indoc!(
r" r"
f : Num.Int * -> Num.Int * f : Num.Int * -> Num.Int *
f = \ a -> a f = | a| a
f f
" "
@ -300,7 +300,7 @@ mod test_can {
let src = indoc!( let src = indoc!(
r" r"
f : Num.Int * -> Num.Int * # comment f : Num.Int * -> Num.Int * # comment
f = \ a -> a f = | a| a
f f
" "
@ -316,7 +316,7 @@ mod test_can {
let src = indoc!( let src = indoc!(
r" r"
f : Num.Int * -> Num.Int * f : Num.Int * -> Num.Int *
g = \ a -> a g = | a| a
g g
" "
@ -344,7 +344,7 @@ mod test_can {
let src = indoc!( let src = indoc!(
r" r"
f : Num.Int * -> Num.Int * # comment f : Num.Int * -> Num.Int * # comment
g = \ a -> a g = | a| a
g g
" "
@ -373,7 +373,7 @@ mod test_can {
r" r"
f : Num.Int * -> Num.Int * f : Num.Int * -> Num.Int *
f = \ a -> a f = | a| a
f 42 f 42
" "
@ -390,7 +390,7 @@ mod test_can {
r" r"
f : Num.Int * -> Num.Int * f : Num.Int * -> Num.Int *
# comment # comment
f = \ a -> a f = | a| a
f 42 f 42
" "
@ -677,8 +677,8 @@ mod test_can {
fn record_builder_desugar() { fn record_builder_desugar() {
let src = indoc!( let src = indoc!(
r#" r#"
map2 = \a, b, combine -> combine a b map2 = |a, b, combine| combine a b
double = \n -> n * 2 double = |n| n * 2
c = 3 c = 3
@ -972,10 +972,10 @@ mod test_can {
} }
#[test] #[test]
fn try_desugar_double_question_suffix() { fn try_desugar_double_question_binop() {
let src = indoc!( let src = indoc!(
r#" r#"
Str.to_u64 "123" ?? Num.max_u64 Str.to_u64("123") ?? Num.max_u64
"# "#
); );
let arena = Bump::new(); let arena = Bump::new();
@ -985,33 +985,95 @@ mod test_can {
// Assert that we desugar to: // Assert that we desugar to:
// //
// when Str.to_u64 "123" // when Str.to_u64("123")
// Ok success_BRANCH1_0_9 -> success_BRANCH1_0_9 // Ok(#double_question_ok_0_17) -> Ok(#double_question_ok_0_17)
// Err _ -> Num.max_u64 // Err(_) -> Num.max_u64
let (cond_expr, branches) = assert_when(&out.loc_expr.value); let (cond_expr, branches) = assert_when(&out.loc_expr.value);
let cond_args = assert_func_call(cond_expr, "to_u64", CalledVia::Space, &out.interns); let cond_args = assert_func_call(cond_expr, "to_u64", CalledVia::Space, &out.interns);
assert_eq!(cond_args.len(), 1); assert_eq!(cond_args.len(), 1);
assert_str_value(&cond_args[0].1.value, "123"); assert_str_value(&cond_args[0].1.value, "123");
assert_eq!(branches.len(), 2); assert_eq!(branches.len(), 2);
assert_eq!(branches[0].patterns.len(), 1); assert_eq!(branches[0].patterns.len(), 1);
assert_eq!(branches[1].patterns.len(), 1); assert_eq!(branches[1].patterns.len(), 1);
assert_pattern_tag_apply_with_ident( assert_pattern_tag_apply_with_ident(
&branches[0].patterns[0].pattern.value, &branches[0].patterns[0].pattern.value,
"Ok", "Ok",
"success_BRANCH1_0_16", "#double_question_ok_0_17",
&out.interns, &out.interns,
); );
assert_var_usage( assert_var_usage(
&branches[0].value.value, &branches[0].value.value,
"success_BRANCH1_0_16", "#double_question_ok_0_17",
&out.interns, &out.interns,
); );
assert_pattern_tag_apply_with_underscore(&branches[1].patterns[0].pattern.value, "Err"); assert_pattern_tag_apply_with_underscore(&branches[1].patterns[0].pattern.value, "Err");
assert_var_usage(&branches[1].value.value, "max_u64", &out.interns); assert_var_usage(&branches[1].value.value, "max_u64", &out.interns);
} }
#[test]
fn try_desugar_single_question_binop() {
let src = indoc!(
r#"
Str.to_u64("123") ? FailedToConvert
"#
);
let arena = Bump::new();
let out = can_expr_with(&arena, test_home(), src);
assert_eq!(out.problems, Vec::new());
// Assert that we desugar to:
//
// when Str.to_u64("123")
// Ok(#single_question_ok_0_17) -> #single_question_ok_0_17
// Err(#single_question_err_0_17) -> return Err(FailedToConvert(#single_question_err_0_17))
let (cond_expr, branches) = assert_when(&out.loc_expr.value);
let cond_args = assert_func_call(cond_expr, "to_u64", CalledVia::Space, &out.interns);
assert_eq!(cond_args.len(), 1);
assert_str_value(&cond_args[0].1.value, "123");
assert_eq!(branches.len(), 2);
assert_eq!(branches[0].patterns.len(), 1);
assert_eq!(branches[1].patterns.len(), 1);
assert_pattern_tag_apply_with_ident(
&branches[0].patterns[0].pattern.value,
"Ok",
"#single_question_ok_0_17",
&out.interns,
);
assert_var_usage(
&branches[0].value.value,
"#single_question_ok_0_17",
&out.interns,
);
assert_pattern_tag_apply_with_ident(
&branches[1].patterns[0].pattern.value,
"Err",
"#single_question_err_0_17",
&out.interns,
);
let err_expr = assert_return_expr(&branches[1].value.value);
let mapped_err = assert_tag_application(err_expr, "Err");
assert_eq!(mapped_err.len(), 1);
let inner_err = assert_tag_application(&mapped_err[0].1.value, "FailedToConvert");
assert_eq!(inner_err.len(), 1);
assert_var_usage(
&inner_err[0].1.value,
"#single_question_err_0_17",
&out.interns,
);
}
#[test] #[test]
fn try_desugar_works_elsewhere() { fn try_desugar_works_elsewhere() {
let src = indoc!( let src = indoc!(
@ -1192,6 +1254,13 @@ mod test_can {
} }
} }
fn assert_return_expr(expr: &Expr) -> &Expr {
match expr {
Expr::Return { return_value, .. } => &return_value.value,
_ => panic!("Expr was not a Return: {:?}", expr),
}
}
// TAIL CALLS // TAIL CALLS
fn get_closure(expr: &Expr, i: usize) -> roc_can::expr::Recursive { fn get_closure(expr: &Expr, i: usize) -> roc_can::expr::Recursive {
match expr { match expr {
@ -1241,20 +1310,20 @@ mod test_can {
fn recognize_tail_calls() { fn recognize_tail_calls() {
let src = indoc!( let src = indoc!(
r" r"
g = \x -> g = |x|
when x is when x is
0 -> 0 0 -> 0
_ -> g (x - 1) _ -> g (x - 1)
# use parens to force the ordering! # use parens to force the ordering!
( (
h = \x -> h = |x|
when x is when x is
0 -> 0 0 -> 0
_ -> g (x - 1) _ -> g (x - 1)
( (
p = \x -> p = |x|
when x is when x is
0 -> 0 0 -> 0
1 -> g (x - 1) 1 -> g (x - 1)
@ -1342,7 +1411,7 @@ mod test_can {
fn when_tail_call() { fn when_tail_call() {
let src = indoc!( let src = indoc!(
r" r"
g = \x -> g = |x|
when x is when x is
0 -> 0 0 -> 0
_ -> g (x + 1) _ -> g (x + 1)
@ -1364,7 +1433,7 @@ mod test_can {
fn immediate_tail_call() { fn immediate_tail_call() {
let src = indoc!( let src = indoc!(
r" r"
f = \x -> f x f = |x| f x
f 0 f 0
" "
@ -1385,7 +1454,7 @@ mod test_can {
fn when_condition_is_no_tail_call() { fn when_condition_is_no_tail_call() {
let src = indoc!( let src = indoc!(
r" r"
q = \x -> q = |x|
when q x is when q x is
_ -> 0 _ -> 0
@ -1406,12 +1475,12 @@ mod test_can {
fn good_mutual_recursion() { fn good_mutual_recursion() {
let src = indoc!( let src = indoc!(
r" r"
q = \x -> q = |x|
when x is when x is
0 -> 0 0 -> 0
_ -> p (x - 1) _ -> p (x - 1)
p = \x -> p = |x|
when x is when x is
0 -> 0 0 -> 0
_ -> q (x - 1) _ -> q (x - 1)
@ -1437,7 +1506,7 @@ mod test_can {
fn valid_self_recursion() { fn valid_self_recursion() {
let src = indoc!( let src = indoc!(
r" r"
boom = \_ -> boom {} boom = |_| boom {}
boom boom
" "
@ -1548,7 +1617,7 @@ mod test_can {
r" r"
fallbackZ = 3 fallbackZ = 3
fn = \{ x, y, z ? fallbackZ } -> fn = |{ x, y, z ? fallbackZ }|
{ x, y, z } { x, y, z }
fn { x: 0, y: 1 } fn { x: 0, y: 1 }

View file

@ -1,11 +1,11 @@
use crate::{ use crate::{
collection::{fmt_collection, Braces}, collection::{fmt_collection, Braces},
expr::merge_spaces_conservative, expr::{expr_lift_spaces, expr_lift_spaces_after, expr_prec, merge_spaces_conservative},
node::{ node::{
parens_around_node, DelimitedItem, Item, Node, NodeInfo, NodeSequenceBuilder, Nodify, Prec, parens_around_node, DelimitedItem, Item, Node, NodeInfo, NodeSequenceBuilder, Nodify, Prec,
Sp, Sp,
}, },
pattern::{pattern_lift_spaces_after, snakify_camel_ident}, pattern::snakify_camel_ident,
spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT}, spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT},
Buf, MigrationFlags, Buf, MigrationFlags,
}; };
@ -13,7 +13,6 @@ use bumpalo::{
collections::{String, Vec}, collections::{String, Vec},
Bump, Bump,
}; };
use roc_parse::{ast::Spaced, ident::UppercaseIdent};
use roc_parse::{ use roc_parse::{
ast::{ ast::{
AbilityImpls, AssignedField, Collection, CommentOrNewline, Expr, ExtractSpaces, AbilityImpls, AssignedField, Collection, CommentOrNewline, Expr, ExtractSpaces,
@ -22,6 +21,10 @@ use roc_parse::{
}, },
expr::merge_spaces, expr::merge_spaces,
}; };
use roc_parse::{
ast::{Spaced, TypeVar},
ident::UppercaseIdent,
};
use roc_region::all::Loc; use roc_region::all::Loc;
/// Does an AST node need parens around it? /// Does an AST node need parens around it?
@ -966,7 +969,7 @@ pub fn type_head_lift_spaces_after<'a, 'b: 'a>(
) -> SpacesAfter<'a, TypeHeader<'a>> { ) -> SpacesAfter<'a, TypeHeader<'a>> {
let new_vars = arena.alloc_slice_copy(header.vars); let new_vars = arena.alloc_slice_copy(header.vars);
let after = if let Some(last) = new_vars.last_mut() { let after = if let Some(last) = new_vars.last_mut() {
let lifted = pattern_lift_spaces_after(arena, &last.value); let lifted = type_var_lift_spaces_after(arena, last.value);
last.value = lifted.item; last.value = lifted.item;
lifted.after lifted.after
} else { } else {
@ -981,6 +984,135 @@ pub fn type_head_lift_spaces_after<'a, 'b: 'a>(
} }
} }
fn type_var_lift_spaces_after<'a, 'b: 'a>(
arena: &'a Bump,
var: TypeVar<'b>,
) -> SpacesAfter<'a, TypeVar<'a>> {
match var {
item @ TypeVar::Identifier(_) => SpacesAfter { item, after: &[] },
TypeVar::Malformed(expr) => {
let lifted = expr_lift_spaces_after(Parens::NotNeeded, arena, expr);
SpacesAfter {
item: TypeVar::Malformed(arena.alloc(lifted.item)),
after: lifted.after,
}
}
TypeVar::SpaceBefore(inner, spaces) => {
let lifted = type_var_lift_spaces_after(arena, *inner);
SpacesAfter {
item: TypeVar::SpaceBefore(arena.alloc(lifted.item), spaces),
after: lifted.after,
}
}
TypeVar::SpaceAfter(inner, spaces) => {
let mut lifted = type_var_lift_spaces_after(arena, *inner);
lifted.after = merge_spaces_conservative(arena, lifted.after, spaces);
lifted
}
}
}
impl<'a> Formattable for TypeHeader<'a> {
fn is_multiline(&self) -> bool {
self.vars.iter().any(|v| v.is_multiline())
}
fn format_with_options(
&self,
buf: &mut Buf,
_parens: Parens,
_newlines: Newlines,
indent: u16,
) {
let node = self.to_node(buf.text.bump(), buf.flags());
node.format(buf, indent);
}
}
impl<'a> Formattable for TypeVar<'a> {
fn is_multiline(&self) -> bool {
match self {
TypeVar::Identifier(_) => false,
TypeVar::Malformed(expr) => expr.is_multiline(),
TypeVar::SpaceBefore(inner, spaces) | TypeVar::SpaceAfter(inner, spaces) => {
inner.is_multiline() || !spaces.is_empty()
}
}
}
fn format_with_options(
&self,
buf: &mut Buf,
_parens: Parens,
_newlines: Newlines,
indent: u16,
) {
let node = self.to_node(buf.text.bump(), buf.flags());
node.format(buf, indent);
}
}
impl<'a> Nodify<'a> for TypeHeader<'a> {
fn to_node<'b>(&'a self, arena: &'b Bump, flags: MigrationFlags) -> NodeInfo<'b>
where
'a: 'b,
{
NodeInfo::apply(
arena,
NodeInfo::item(Node::Literal(self.name.value)),
self.vars.iter().map(|v| v.value.to_node(arena, flags)),
)
}
}
impl<'a> Nodify<'a> for TypeVar<'a> {
fn to_node<'b>(&'a self, arena: &'b Bump, flags: MigrationFlags) -> NodeInfo<'b>
where
'a: 'b,
{
match self {
TypeVar::SpaceBefore(inner, spaces) => {
let mut inner = inner.to_node(arena, flags);
inner.before = merge_spaces_conservative(arena, spaces, inner.before);
inner
}
TypeVar::SpaceAfter(inner, spaces) => {
let mut inner = inner.to_node(arena, flags);
inner.after = merge_spaces_conservative(arena, inner.after, spaces);
inner
}
TypeVar::Identifier(text) => {
let var_name = if flags.snakify {
let mut buf = Buf::new_in(arena, flags);
buf.indent(0); // Take out of beginning of line
snakify_camel_ident(&mut buf, text);
let s: &str = arena.alloc_str(buf.as_str());
s
} else {
text
};
let item = NodeInfo::item(Node::Literal(var_name));
if *text == "implements" {
parens_around_node(arena, item, false)
} else {
item
}
}
TypeVar::Malformed(expr) => {
let lifted = expr_lift_spaces(Parens::InApply, arena, expr);
NodeInfo {
before: lifted.before,
node: Node::Expr(lifted.item),
after: lifted.after,
needs_indent: true,
prec: expr_prec(**expr),
}
}
}
}
}
impl<'a> Nodify<'a> for TypeAnnotation<'a> { impl<'a> Nodify<'a> for TypeAnnotation<'a> {
fn to_node<'b>(&'a self, arena: &'b Bump, flags: MigrationFlags) -> NodeInfo<'b> fn to_node<'b>(&'a self, arena: &'b Bump, flags: MigrationFlags) -> NodeInfo<'b>
where where

View file

@ -7,20 +7,19 @@ use crate::expr::{
expr_lift_and_lower, expr_lift_spaces, expr_lift_spaces_after, expr_lift_spaces_before, expr_lift_and_lower, expr_lift_spaces, expr_lift_spaces_after, expr_lift_spaces_before,
fmt_str_literal, is_str_multiline, merge_spaces_conservative, sub_expr_requests_parens, fmt_str_literal, is_str_multiline, merge_spaces_conservative, sub_expr_requests_parens,
}; };
use crate::node::{NodeInfo, Nodify}; use crate::node::Nodify;
use crate::pattern::{pattern_apply_to_node, pattern_fmt_apply}; use crate::pattern::pattern_lift_spaces_before;
use crate::pattern::{pattern_lift_spaces, pattern_lift_spaces_before};
use crate::spaces::{ use crate::spaces::{
fmt_comments_only, fmt_default_newline, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT, fmt_comments_only, fmt_default_newline, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT,
}; };
use crate::{Buf, MigrationFlags}; use crate::Buf;
use bumpalo::Bump; use bumpalo::Bump;
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
use roc_parse::ast::{ use roc_parse::ast::{
AbilityMember, Defs, Expr, ExtractSpaces, ImportAlias, ImportAsKeyword, ImportExposingKeyword, AbilityMember, Defs, Expr, ExtractSpaces, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
ModuleImportParams, Pattern, Spaces, SpacesBefore, StrLiteral, TypeAnnotation, TypeDef, ModuleImportParams, Pattern, Spaces, SpacesBefore, StrLiteral, TypeAnnotation, TypeDef,
TypeHeader, ValueDef, ValueDef,
}; };
use roc_parse::expr::merge_spaces; use roc_parse::expr::merge_spaces;
use roc_parse::header::Keyword; use roc_parse::header::Keyword;
@ -474,8 +473,8 @@ impl<'a> Formattable for TypeDef<'a> {
loc_implements, loc_implements,
members, members,
} => { } => {
let header_lifted = type_head_lift_spaces(buf.text.bump(), *header); let header_lifted = header.to_node(buf.text.bump(), buf.flags());
header_lifted.item.format(buf, indent); header_lifted.node.format(buf, indent);
let implements = loc_implements.value.extract_spaces(); let implements = loc_implements.value.extract_spaces();
let before_implements = merge_spaces_conservative( let before_implements = merge_spaces_conservative(
buf.text.bump(), buf.text.bump(),
@ -532,57 +531,6 @@ impl<'a> Formattable for TypeDef<'a> {
} }
} }
impl<'a> Formattable for TypeHeader<'a> {
fn is_multiline(&self) -> bool {
self.vars.iter().any(|v| v.is_multiline())
}
fn format_with_options(
&self,
buf: &mut Buf,
_parens: Parens,
_newlines: Newlines,
indent: u16,
) {
let old_flags = buf.flags;
buf.flags = MigrationFlags {
parens_and_commas: false,
..old_flags
};
pattern_fmt_apply(
buf,
Pattern::Tag(self.name.value),
self.vars,
Parens::NotNeeded,
indent,
self.vars.iter().any(|v| v.is_multiline()),
false,
None,
);
buf.flags = old_flags;
}
}
fn type_head_lift_spaces<'a, 'b: 'a>(
arena: &'a Bump,
head: TypeHeader<'b>,
) -> Spaces<'a, Pattern<'a>> {
let pat = Pattern::Apply(
arena.alloc(Loc::at(head.name.region, Pattern::Tag(head.name.value))),
head.vars,
);
pattern_lift_spaces(arena, &pat)
}
impl<'a> Nodify<'a> for TypeHeader<'a> {
fn to_node<'b>(&'a self, arena: &'b Bump, _flags: MigrationFlags) -> NodeInfo<'b>
where
'a: 'b,
{
pattern_apply_to_node(arena, Pattern::Tag(self.name.value), self.vars)
}
}
impl<'a> Formattable for ModuleImport<'a> { impl<'a> Formattable for ModuleImport<'a> {
fn is_multiline(&self) -> bool { fn is_multiline(&self) -> bool {
let Self { let Self {

View file

@ -1,6 +1,7 @@
use crate::annotation::{except_last, is_collection_multiline, Formattable, Newlines, Parens}; use crate::annotation::{except_last, is_collection_multiline, Formattable, Newlines, Parens};
use crate::collection::{fmt_collection, Braces}; use crate::collection::{fmt_collection, Braces};
use crate::def::{fmt_defs, valdef_lift_spaces_before}; use crate::def::{fmt_defs, valdef_lift_spaces_before};
use crate::node::Prec;
use crate::pattern::{ use crate::pattern::{
fmt_pattern, pattern_lift_spaces, snakify_camel_ident, starts_with_inline_comment, fmt_pattern, pattern_lift_spaces, snakify_camel_ident, starts_with_inline_comment,
}; };
@ -962,6 +963,7 @@ fn push_op(buf: &mut Buf, op: BinOp) {
called_via::BinOp::Or => buf.push_str("||"), called_via::BinOp::Or => buf.push_str("||"),
called_via::BinOp::Pizza => buf.push_str("|>"), called_via::BinOp::Pizza => buf.push_str("|>"),
called_via::BinOp::DoubleQuestion => buf.push_str("??"), called_via::BinOp::DoubleQuestion => buf.push_str("??"),
called_via::BinOp::SingleQuestion => buf.push_str("?"),
} }
} }
@ -1390,11 +1392,7 @@ pub fn expr_lift_spaces<'a, 'b: 'a>(
before: &[], before: &[],
item: *expr, item: *expr,
after: &[], after: &[],
}, // _ => Spaces { },
// before: &[],
// item: *expr,
// after: &[],
// },
} }
} }
@ -1410,6 +1408,54 @@ pub fn expr_lift_spaces_before<'a, 'b: 'a>(
} }
} }
pub fn expr_prec(expr: Expr<'_>) -> Prec {
match expr {
Expr::Float(_)
| Expr::Num(_)
| Expr::NonBase10Int { .. }
| Expr::Str(_)
| Expr::SingleQuote(_)
| Expr::AccessorFunction(_)
| Expr::RecordUpdater(_)
| Expr::Var { .. }
| Expr::Underscore(_)
| Expr::Crash
| Expr::Tag(_)
| Expr::OpaqueRef(_)
| Expr::Dbg
| Expr::Try
| Expr::MalformedIdent(_, _)
| Expr::EmptyRecordBuilder(_)
| Expr::SingleFieldRecordBuilder(_)
| Expr::RecordAccess(_, _)
| Expr::TupleAccess(_, _)
| Expr::TrySuffix { .. }
| Expr::List(_)
| Expr::RecordUpdate { .. }
| Expr::Record(_)
| Expr::Tuple(_)
| Expr::RecordBuilder { .. }
| Expr::LowLevelTry(_, _)
| Expr::LowLevelDbg(_, _, _)
| Expr::PncApply(_, _)
| Expr::OptionalFieldInRecordBuilder(_, _) => Prec::Term,
Expr::Closure(_, _)
| Expr::Defs(_, _)
| Expr::DbgStmt { .. }
| Expr::Apply(_, _, _)
| Expr::BinOps(_, _)
| Expr::UnaryOp(_, _)
| Expr::If { .. }
| Expr::When(_, _)
| Expr::Return(_, _)
| Expr::SpaceBefore(_, _)
| Expr::SpaceAfter(_, _)
| Expr::ParensAround(_)
| Expr::PrecedenceConflict(_) => Prec::Apply,
}
}
pub fn expr_lift_spaces_after<'a, 'b: 'a>( pub fn expr_lift_spaces_after<'a, 'b: 'a>(
parens: Parens, parens: Parens,
arena: &'a Bump, arena: &'a Bump,
@ -1969,7 +2015,7 @@ fn fmt_closure<'a>(
use self::Expr::*; use self::Expr::*;
buf.indent(indent); buf.indent(indent);
buf.push('\\'); buf.push('|');
let arguments_are_multiline = loc_patterns let arguments_are_multiline = loc_patterns
.iter() .iter()
@ -2017,12 +2063,10 @@ fn fmt_closure<'a>(
if arguments_are_multiline { if arguments_are_multiline {
buf.ensure_ends_with_newline(); buf.ensure_ends_with_newline();
buf.indent(indent); buf.indent(indent);
} else {
buf.spaces(1);
} }
let arrow_line_indent = buf.cur_line_indent(); let arrow_line_indent = buf.cur_line_indent();
buf.push_str("->"); buf.push_str("|");
buf.spaces(1); buf.spaces(1);
let is_multiline = loc_ret.value.is_multiline(); let is_multiline = loc_ret.value.is_multiline();
@ -2239,7 +2283,8 @@ pub fn sub_expr_requests_parens(expr: &Expr<'_>) -> bool {
| BinOp::And | BinOp::And
| BinOp::Or | BinOp::Or
| BinOp::Pizza | BinOp::Pizza
| BinOp::DoubleQuestion => true, | BinOp::DoubleQuestion
| BinOp::SingleQuestion => true,
}) })
} }
Expr::If { .. } => true, Expr::If { .. } => true,

View file

@ -1,5 +1,5 @@
use bumpalo::{collections::Vec, Bump}; use bumpalo::{collections::Vec, Bump};
use roc_parse::ast::{CommentOrNewline, Pattern, TypeAnnotation}; use roc_parse::ast::{CommentOrNewline, Expr, Pattern, TypeAnnotation};
use crate::{ use crate::{
annotation::{Formattable, Newlines, Parens}, annotation::{Formattable, Newlines, Parens},
@ -96,6 +96,7 @@ pub enum Node<'a> {
// Temporary! TODO: translate these into proper Node elements // Temporary! TODO: translate these into proper Node elements
TypeAnnotation(TypeAnnotation<'a>), TypeAnnotation(TypeAnnotation<'a>),
Pattern(Pattern<'a>), Pattern(Pattern<'a>),
Expr(Expr<'a>),
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -321,6 +322,24 @@ fn fmt_sp(buf: &mut Buf, sp: Sp<'_>, indent: u16) {
} }
} }
impl<'a> Formattable for NodeInfo<'a> {
fn is_multiline(&self) -> bool {
!self.before.is_empty() || self.node.is_multiline() || !self.after.is_empty()
}
fn format_with_options(
&self,
buf: &mut Buf,
_parens: Parens,
_newlines: Newlines,
indent: u16,
) {
fmt_spaces(buf, self.before.iter(), indent);
self.node.format(buf, indent);
fmt_spaces(buf, self.after.iter(), indent);
}
}
impl<'a> Formattable for Node<'a> { impl<'a> Formattable for Node<'a> {
fn is_multiline(&self) -> bool { fn is_multiline(&self) -> bool {
match self { match self {
@ -350,6 +369,7 @@ impl<'a> Formattable for Node<'a> {
Node::Literal(_) => false, Node::Literal(_) => false,
Node::TypeAnnotation(type_annotation) => type_annotation.is_multiline(), Node::TypeAnnotation(type_annotation) => type_annotation.is_multiline(),
Node::Pattern(pat) => pat.is_multiline(), Node::Pattern(pat) => pat.is_multiline(),
Node::Expr(expr) => expr.is_multiline(),
} }
} }
@ -452,6 +472,9 @@ impl<'a> Formattable for Node<'a> {
Node::Pattern(pat) => { Node::Pattern(pat) => {
pat.format_with_options(buf, parens, newlines, indent); pat.format_with_options(buf, parens, newlines, indent);
} }
Node::Expr(expr) => {
expr.format_with_options(buf, parens, newlines, indent);
}
} }
} }
} }

View file

@ -91,6 +91,7 @@ impl<'a> Formattable for Pattern<'a> {
| Pattern::Underscore(_) | Pattern::Underscore(_)
| Pattern::Malformed(_) | Pattern::Malformed(_)
| Pattern::MalformedIdent(_, _) | Pattern::MalformedIdent(_, _)
| Pattern::MalformedExpr(_)
| Pattern::QualifiedIdentifier { .. } => false, | Pattern::QualifiedIdentifier { .. } => false,
Pattern::Tuple(patterns) | Pattern::List(patterns) => { Pattern::Tuple(patterns) | Pattern::List(patterns) => {
@ -447,6 +448,10 @@ fn fmt_pattern_only(
buf.indent(indent); buf.indent(indent);
buf.push_str(string); buf.push_str(string);
} }
Pattern::MalformedExpr(expr) => {
buf.indent(indent);
expr.format(buf, indent);
}
Pattern::QualifiedIdentifier { module_name, ident } => { Pattern::QualifiedIdentifier { module_name, ident } => {
buf.indent(indent); buf.indent(indent);
if !module_name.is_empty() { if !module_name.is_empty() {
@ -538,20 +543,6 @@ pub fn pattern_fmt_apply(
let mut before = merge_spaces(buf.text.bump(), last_after, arg.before); let mut before = merge_spaces(buf.text.bump(), last_after, arg.before);
if !before.is_empty() { if !before.is_empty() {
if starts_with_block_str(&arg.item) {
// Ick!
// The block string will keep "generating" newlines when formatted (it wants to start on its own line),
// so we strip one out here.
//
// Note that this doesn't affect Expr's because those have explicit parens, and we can control
// whether spaces cross that boundary.
let chop_off = before
.iter()
.rev()
.take_while(|&&s| matches!(s, CommentOrNewline::Newline))
.count();
before = &before[..before.len() - chop_off];
}
handle_multiline_str_spaces(&arg.item, &mut before); handle_multiline_str_spaces(&arg.item, &mut before);
if !is_multiline { if !is_multiline {
@ -674,7 +665,9 @@ fn pattern_prec(pat: Pattern<'_>) -> Prec {
| Pattern::PncApply(_, _) => Prec::Term, | Pattern::PncApply(_, _) => Prec::Term,
Pattern::Apply(_, _) | Pattern::As(_, _) => Prec::Apply, Pattern::Apply(_, _) | Pattern::As(_, _) => Prec::Apply,
Pattern::SpaceBefore(inner, _) | Pattern::SpaceAfter(inner, _) => pattern_prec(*inner), Pattern::SpaceBefore(inner, _) | Pattern::SpaceAfter(inner, _) => pattern_prec(*inner),
Pattern::Malformed(_) | Pattern::MalformedIdent(..) => Prec::Term, Pattern::Malformed(_) | Pattern::MalformedIdent(..) | Pattern::MalformedExpr(_) => {
Prec::Term
}
} }
} }

View file

@ -4,7 +4,7 @@ use roc_can::scope::Scope;
use roc_collections::VecSet; use roc_collections::VecSet;
use roc_module::ident::ModuleName; use roc_module::ident::ModuleName;
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol}; use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
use roc_parse::ast::{self, ExtractSpaces, TypeHeader}; use roc_parse::ast::{self, ExtractSpaces, TypeHeader, TypeVar};
use roc_parse::ast::{AssignedField, FunctionArrow}; use roc_parse::ast::{AssignedField, FunctionArrow};
use roc_parse::ast::{CommentOrNewline, TypeDef, ValueDef}; use roc_parse::ast::{CommentOrNewline, TypeDef, ValueDef};
@ -312,7 +312,7 @@ fn generate_entry_docs(
let mut type_vars = Vec::new(); let mut type_vars = Vec::new();
for var in vars.iter() { for var in vars.iter() {
if let Pattern::Identifier { ident: ident_name } = var.value { if let TypeVar::Identifier(ident_name) = var.value {
type_vars.push(ident_name.to_string()); type_vars.push(ident_name.to_string());
} }
} }
@ -346,7 +346,7 @@ fn generate_entry_docs(
let mut type_vars = Vec::new(); let mut type_vars = Vec::new();
for var in vars.iter() { for var in vars.iter() {
if let Pattern::Identifier { ident: ident_name } = var.value { if let TypeVar::Identifier(ident_name) = var.value {
type_vars.push(ident_name.to_string()); type_vars.push(ident_name.to_string());
} }
} }
@ -370,7 +370,7 @@ fn generate_entry_docs(
let mut type_vars = Vec::new(); let mut type_vars = Vec::new();
for var in vars.iter() { for var in vars.iter() {
if let Pattern::Identifier { ident: ident_name } = var.value { if let TypeVar::Identifier(ident_name) = var.value {
type_vars.push(ident_name.to_string()); type_vars.push(ident_name.to_string());
} }
} }
@ -631,7 +631,7 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) ->
.vars .vars
.iter() .iter()
.filter_map(|loc_pattern| match loc_pattern.value { .filter_map(|loc_pattern| match loc_pattern.value {
ast::Pattern::Identifier { ident } => Some(ident.to_string()), ast::TypeVar::Identifier(ident) => Some(ident.to_string()),
_ => None, _ => None,
}) })
.collect(), .collect(),

View file

@ -37,7 +37,7 @@ cheapest_open = \cost_function, model ->
Ok(smallest_so_far) Ok(smallest_so_far)
Set.walk(model.open_set, Err(KeyNotFound), folder) Set.walk(model.open_set, Err(KeyNotFound), folder)
|> Result.map(\x -> x.position) |> Result.map_ok(\x -> x.position)
reconstruct_path : Map position position, position -> List position reconstruct_path : Map position position, position -> List position
reconstruct_path = \came_from, goal -> reconstruct_path = \came_from, goal ->

View file

@ -37,7 +37,7 @@ cheapest_open = \cost_function, model ->
Ok(smallest_so_far) Ok(smallest_so_far)
Set.walk(model.open_set, Err(KeyNotFound), folder) Set.walk(model.open_set, Err(KeyNotFound), folder)
|> Result.map(\x -> x.position) |> Result.map_ok(\x -> x.position)
reconstruct_path : Dict position position, position -> List position where position implements Hash & Eq reconstruct_path : Dict position position, position -> List position where position implements Hash & Eq
reconstruct_path = \came_from, goal -> reconstruct_path = \came_from, goal ->

View file

@ -3,7 +3,7 @@ use self::BinOp::*;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt; use std::fmt;
const PRECEDENCES: [(BinOp, u8); 17] = [ const PRECEDENCES: [(BinOp, u8); 18] = [
(Caret, 8), (Caret, 8),
(Star, 7), (Star, 7),
(Slash, 7), (Slash, 7),
@ -12,6 +12,7 @@ const PRECEDENCES: [(BinOp, u8); 17] = [
(Plus, 5), (Plus, 5),
(Minus, 5), (Minus, 5),
(DoubleQuestion, 5), (DoubleQuestion, 5),
(SingleQuestion, 5),
(Pizza, 4), (Pizza, 4),
(Equals, 3), (Equals, 3),
(NotEquals, 3), (NotEquals, 3),
@ -23,7 +24,7 @@ const PRECEDENCES: [(BinOp, u8); 17] = [
(Or, 0), (Or, 0),
]; ];
const ASSOCIATIVITIES: [(BinOp, Associativity); 17] = [ const ASSOCIATIVITIES: [(BinOp, Associativity); 18] = [
(Caret, RightAssociative), (Caret, RightAssociative),
(Star, LeftAssociative), (Star, LeftAssociative),
(Slash, LeftAssociative), (Slash, LeftAssociative),
@ -32,6 +33,7 @@ const ASSOCIATIVITIES: [(BinOp, Associativity); 17] = [
(Plus, LeftAssociative), (Plus, LeftAssociative),
(Minus, LeftAssociative), (Minus, LeftAssociative),
(DoubleQuestion, LeftAssociative), (DoubleQuestion, LeftAssociative),
(SingleQuestion, LeftAssociative),
(Pizza, LeftAssociative), (Pizza, LeftAssociative),
(Equals, NonAssociative), (Equals, NonAssociative),
(NotEquals, NonAssociative), (NotEquals, NonAssociative),
@ -43,7 +45,7 @@ const ASSOCIATIVITIES: [(BinOp, Associativity); 17] = [
(Or, RightAssociative), (Or, RightAssociative),
]; ];
const DISPLAY_STRINGS: [(BinOp, &str); 17] = [ const DISPLAY_STRINGS: [(BinOp, &str); 18] = [
(Caret, "^"), (Caret, "^"),
(Star, "*"), (Star, "*"),
(Slash, "/"), (Slash, "/"),
@ -52,6 +54,7 @@ const DISPLAY_STRINGS: [(BinOp, &str); 17] = [
(Plus, "+"), (Plus, "+"),
(Minus, "-"), (Minus, "-"),
(DoubleQuestion, "??"), (DoubleQuestion, "??"),
(SingleQuestion, "?"),
(Pizza, "|>"), (Pizza, "|>"),
(Equals, "=="), (Equals, "=="),
(NotEquals, "!="), (NotEquals, "!="),
@ -154,6 +157,7 @@ pub enum BinOp {
Plus, Plus,
Minus, Minus,
DoubleQuestion, DoubleQuestion,
SingleQuestion,
Pizza, Pizza,
Equals, Equals,
NotEquals, NotEquals,
@ -170,7 +174,8 @@ impl BinOp {
/// how wide this operator is when typed out /// how wide this operator is when typed out
pub fn width(self) -> u16 { pub fn width(self) -> u16 {
match self { match self {
Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan => 1, Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan
| SingleQuestion => 1,
DoubleSlash | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq | And | Or DoubleSlash | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq | And | Or
| Pizza | DoubleQuestion => 2, | Pizza | DoubleQuestion => 2,
} }
@ -206,13 +211,13 @@ pub enum Associativity {
impl BinOp { impl BinOp {
pub fn associativity(self) -> Associativity { pub fn associativity(self) -> Associativity {
const ASSOCIATIVITY_TABLE: [Associativity; 17] = generate_associativity_table(); const ASSOCIATIVITY_TABLE: [Associativity; 18] = generate_associativity_table();
ASSOCIATIVITY_TABLE[self as usize] ASSOCIATIVITY_TABLE[self as usize]
} }
fn precedence(self) -> u8 { fn precedence(self) -> u8 {
const PRECEDENCE_TABLE: [u8; 17] = generate_precedence_table(); const PRECEDENCE_TABLE: [u8; 18] = generate_precedence_table();
PRECEDENCE_TABLE[self as usize] PRECEDENCE_TABLE[self as usize]
} }
@ -232,14 +237,14 @@ impl Ord for BinOp {
impl std::fmt::Display for BinOp { impl std::fmt::Display for BinOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
const DISPLAY_TABLE: [&str; 17] = generate_display_table(); const DISPLAY_TABLE: [&str; 18] = generate_display_table();
write!(f, "{}", DISPLAY_TABLE[*self as usize]) write!(f, "{}", DISPLAY_TABLE[*self as usize])
} }
} }
const fn generate_precedence_table() -> [u8; 17] { const fn generate_precedence_table() -> [u8; 18] {
let mut table = [0u8; 17]; let mut table = [0u8; 18];
let mut i = 0; let mut i = 0;
while i < PRECEDENCES.len() { while i < PRECEDENCES.len() {
@ -250,8 +255,8 @@ const fn generate_precedence_table() -> [u8; 17] {
table table
} }
const fn generate_associativity_table() -> [Associativity; 17] { const fn generate_associativity_table() -> [Associativity; 18] {
let mut table = [NonAssociative; 17]; let mut table = [NonAssociative; 18];
let mut i = 0; let mut i = 0;
while i < ASSOCIATIVITIES.len() { while i < ASSOCIATIVITIES.len() {
@ -262,8 +267,8 @@ const fn generate_associativity_table() -> [Associativity; 17] {
table table
} }
const fn generate_display_table() -> [&'static str; 17] { const fn generate_display_table() -> [&'static str; 18] {
let mut table = [""; 17]; let mut table = [""; 18];
let mut i = 0; let mut i = 0;
while i < DISPLAY_STRINGS.len() { while i < DISPLAY_STRINGS.len() {

View file

@ -1525,7 +1525,7 @@ define_builtins! {
0 RESULT_RESULT: "Result" exposed_type=true // the Result.Result type alias 0 RESULT_RESULT: "Result" exposed_type=true // the Result.Result type alias
1 RESULT_IS_ERR: "is_err" 1 RESULT_IS_ERR: "is_err"
2 RESULT_ON_ERR: "on_err" 2 RESULT_ON_ERR: "on_err"
3 RESULT_MAP: "map" 3 RESULT_MAP_OK: "map_ok"
4 RESULT_MAP_ERR: "map_err" 4 RESULT_MAP_ERR: "map_err"
5 RESULT_WITH_DEFAULT: "with_default" 5 RESULT_WITH_DEFAULT: "with_default"
6 RESULT_TRY: "try" 6 RESULT_TRY: "try"

View file

@ -633,7 +633,7 @@ pub struct PrecedenceConflict<'a> {
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct TypeHeader<'a> { pub struct TypeHeader<'a> {
pub name: Loc<&'a str>, pub name: Loc<&'a str>,
pub vars: &'a [Loc<Pattern<'a>>], pub vars: &'a [Loc<TypeVar<'a>>],
} }
impl<'a> TypeHeader<'a> { impl<'a> TypeHeader<'a> {
@ -646,6 +646,17 @@ impl<'a> TypeHeader<'a> {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TypeVar<'a> {
Identifier(&'a str),
SpaceBefore(&'a TypeVar<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a TypeVar<'a>, &'a [CommentOrNewline<'a>]),
// These are syntactically parsed as exprs first, so if there's anything else here,
// we consider it malformed but preserve it for error reporting and more resilient parsing.
Malformed(&'a Expr<'a>),
}
/// The `implements` keyword associated with ability definitions. /// The `implements` keyword associated with ability definitions.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum Implements<'a> { pub enum Implements<'a> {
@ -1642,6 +1653,7 @@ pub enum Pattern<'a> {
// Malformed // Malformed
Malformed(&'a str), Malformed(&'a str),
MalformedIdent(&'a str, crate::ident::BadIdent), MalformedIdent(&'a str, crate::ident::BadIdent),
MalformedExpr(&'a Expr<'a>),
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@ -1868,6 +1880,11 @@ impl<'a> Pattern<'a> {
false false
} }
} }
MalformedExpr(_expr_x) => {
// conservatively assume all malformed expr patterns are not-equivalient
false
}
} }
} }
} }
@ -2256,6 +2273,7 @@ impl_extract_spaces!(AssignedField<T>);
impl_extract_spaces!(TypeAnnotation); impl_extract_spaces!(TypeAnnotation);
impl_extract_spaces!(ImplementsAbility); impl_extract_spaces!(ImplementsAbility);
impl_extract_spaces!(Implements); impl_extract_spaces!(Implements);
impl_extract_spaces!(TypeVar);
impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> { impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
type Item = T; type Item = T;
@ -2560,6 +2578,7 @@ impl<'a> Malformed for Pattern<'a> {
Malformed(_) | Malformed(_) |
MalformedIdent(_, _) | MalformedIdent(_, _) |
MalformedExpr(_) |
QualifiedIdentifier { .. } => true, QualifiedIdentifier { .. } => true,
} }
} }
@ -2730,6 +2749,16 @@ impl<'a> Malformed for TypeHeader<'a> {
} }
} }
impl<'a> Malformed for TypeVar<'a> {
fn is_malformed(&self) -> bool {
match self {
TypeVar::Identifier(_) => false,
TypeVar::Malformed(_) => true,
TypeVar::SpaceBefore(var, _) | TypeVar::SpaceAfter(var, _) => var.is_malformed(),
}
}
}
impl<'a> Malformed for Tag<'a> { impl<'a> Malformed for Tag<'a> {
fn is_malformed(&self) -> bool { fn is_malformed(&self) -> bool {
match self { match self {

View file

@ -2,7 +2,8 @@ use crate::ast::{
AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, Implements, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, Implements,
ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword, ImportedModuleName, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword, ImportedModuleName,
IngestedFileAnnotation, IngestedFileImport, ModuleImport, ModuleImportParams, Pattern, IngestedFileAnnotation, IngestedFileImport, ModuleImport, ModuleImportParams, Pattern,
Spaceable, Spaced, Spaces, SpacesBefore, TypeAnnotation, TypeDef, TypeHeader, ValueDef, Spaceable, Spaced, Spaces, SpacesBefore, TypeAnnotation, TypeDef, TypeHeader, TypeVar,
ValueDef,
}; };
use crate::blankspace::{ use crate::blankspace::{
loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e, loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e,
@ -17,8 +18,8 @@ use crate::parser::{
collection_trailing_sep_e, either, increment_min_indent, indented_seq_skip_first, loc, map, collection_trailing_sep_e, either, increment_min_indent, indented_seq_skip_first, loc, map,
map_with_arena, optional, reset_min_indent, sep_by1, sep_by1_e, set_min_indent, skip_first, map_with_arena, optional, reset_min_indent, sep_by1, sep_by1_e, set_min_indent, skip_first,
skip_second, specialize_err, specialize_err_ref, then, two_bytes, zero_or_more, EClosure, skip_second, specialize_err, specialize_err_ref, then, two_bytes, zero_or_more, EClosure,
EExpect, EExpr, EIf, EImport, EImportParams, EInParens, EList, ENumber, EPattern, ERecord, EExpect, EExpr, EIf, EImport, EImportParams, EInParens, EList, ENumber, ERecord, EReturn,
EReturn, EString, EType, EWhen, Either, ParseResult, Parser, SpaceProblem, EString, EType, EWhen, Either, ParseResult, Parser, SpaceProblem,
}; };
use crate::pattern::closure_param; use crate::pattern::closure_param;
use crate::state::State; use crate::state::State;
@ -751,7 +752,7 @@ fn parse_stmt_operator_chain<'a>(
// try an operator // try an operator
return parse_stmt_after_apply( return parse_stmt_after_apply(
arena, arena,
state, state.clone(),
min_indent, min_indent,
call_min_indent, call_min_indent,
expr_state, expr_state,
@ -1244,21 +1245,11 @@ fn parse_stmt_alias_or_opaque<'a>(
let mut type_arguments = Vec::with_capacity_in(arguments.len(), arena); let mut type_arguments = Vec::with_capacity_in(arguments.len(), arena);
for argument in arguments { for argument in arguments {
match expr_to_pattern_help(arena, &argument.value) { type_arguments.push(Loc::at(
Ok(good) => { argument.region,
type_arguments.push(Loc::at(argument.region, good)); expr_to_type_var(arena, &argument.value),
}
Err(()) => {
return Err((
MadeProgress,
EExpr::Pattern(
arena.alloc(EPattern::NotAPattern(state.pos())),
state.pos(),
),
)); ));
} }
}
}
match kind.value { match kind.value {
AliasOrOpaque::Alias => { AliasOrOpaque::Alias => {
@ -1344,6 +1335,29 @@ fn parse_stmt_alias_or_opaque<'a>(
Ok((MadeProgress, res, state)) Ok((MadeProgress, res, state))
} }
fn expr_to_type_var<'a>(arena: &'a Bump, expr: &'a Expr<'a>) -> TypeVar<'a> {
let expr = expr.extract_spaces();
let mut ty = match expr.item {
Expr::Var {
module_name: "",
ident,
} => TypeVar::Identifier(ident),
_ => TypeVar::Malformed(arena.alloc(expr.item)),
};
// Now we re-add the spaces
if !expr.before.is_empty() {
ty = TypeVar::SpaceBefore(arena.alloc(ty), expr.before);
}
if !expr.after.is_empty() {
ty = TypeVar::SpaceAfter(arena.alloc(ty), expr.after);
}
ty
}
mod ability { mod ability {
use parser::absolute_indented_seq; use parser::absolute_indented_seq;
@ -1464,7 +1478,7 @@ mod ability {
fn finish_parsing_ability_def_help<'a>( fn finish_parsing_ability_def_help<'a>(
call_min_indent: u32, call_min_indent: u32,
name: Loc<&'a str>, name: Loc<&'a str>,
args: &'a [Loc<Pattern<'a>>], args: &'a [Loc<TypeVar<'a>>],
loc_implements: Loc<Implements<'a>>, loc_implements: Loc<Implements<'a>>,
arena: &'a Bump, arena: &'a Bump,
state: State<'a>, state: State<'a>,
@ -1888,6 +1902,10 @@ fn parse_stmt_after_apply<'a>(
) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> { ) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> {
let before_op = state.clone(); let before_op = state.clone();
match loc(operator()).parse(arena, state.clone(), call_min_indent) { match loc(operator()).parse(arena, state.clone(), call_min_indent) {
Err((MadeProgress, EExpr::BadOperator("|", _))) => {
let expr = parse_expr_final(expr_state, arena);
return Ok((MadeProgress, Stmt::Expr(expr), state));
}
Err((MadeProgress, f)) => Err((MadeProgress, f)), Err((MadeProgress, f)) => Err((MadeProgress, f)),
Ok((_, loc_op, state)) => { Ok((_, loc_op, state)) => {
expr_state.consume_spaces(arena); expr_state.consume_spaces(arena);
@ -2028,16 +2046,10 @@ fn parse_ability_def<'a>(
let mut arguments = Vec::with_capacity_in(expr_state.arguments.len(), arena); let mut arguments = Vec::with_capacity_in(expr_state.arguments.len(), arena);
for argument in expr_state.arguments { for argument in expr_state.arguments {
match expr_to_pattern_help(arena, &argument.value) { arguments.push(Loc::at(
Ok(good) => { argument.region,
arguments.push(Loc::at(argument.region, good)); expr_to_type_var(arena, &argument.value),
} ));
Err(_) => {
let start = argument.region.start();
let err = &*arena.alloc(EPattern::Start(start));
return Err((MadeProgress, EExpr::Pattern(err, argument.region.start())));
}
}
} }
// Attach any spaces to the `implements` keyword // Attach any spaces to the `implements` keyword
@ -2327,6 +2339,50 @@ pub fn parse_top_level_defs<'a>(
// PARSER HELPERS // PARSER HELPERS
fn closure_help<'a>(check_for_arrow: CheckForArrow) -> impl Parser<'a, Expr<'a>, EClosure<'a>> { fn closure_help<'a>(check_for_arrow: CheckForArrow) -> impl Parser<'a, Expr<'a>, EClosure<'a>> {
one_of!(
closure_new_syntax_help(),
closure_old_syntax_help(check_for_arrow),
)
}
fn closure_new_syntax_help<'a>() -> impl Parser<'a, Expr<'a>, EClosure<'a>> {
map_with_arena(
indented_seq_skip_first(
error_on_pizza(byte_indent(b'|', EClosure::Bar), EClosure::Start),
and(
sep_by1_e(
byte_indent(b',', EClosure::Comma),
space0_around_ee(
specialize_err(EClosure::Pattern, closure_param()),
EClosure::IndentArg,
EClosure::IndentArrow,
),
EClosure::Arg,
),
skip_first(
// Parse the -> which separates params from body
byte(b'|', EClosure::Bar),
// Parse the body
block(
CheckForArrow(false),
true,
EClosure::IndentBody,
EClosure::Body,
),
),
),
),
|arena: &'a Bump, (params, body)| {
let params: Vec<'a, Loc<Pattern<'a>>> = params;
let params: &'a [Loc<Pattern<'a>>] = params.into_bump_slice();
Expr::Closure(params, arena.alloc(body))
},
)
}
fn closure_old_syntax_help<'a>(
check_for_arrow: CheckForArrow,
) -> impl Parser<'a, Expr<'a>, EClosure<'a>> {
// closure_help_help(options) // closure_help_help(options)
map_with_arena( map_with_arena(
// After the first token, all other tokens must be indented past the start of the line // After the first token, all other tokens must be indented past the start of the line
@ -2363,6 +2419,19 @@ fn closure_help<'a>(check_for_arrow: CheckForArrow) -> impl Parser<'a, Expr<'a>,
) )
} }
fn error_on_pizza<'a, T, E: 'a>(
p: impl Parser<'a, T, E>,
f: impl Fn(Position) -> E,
) -> impl Parser<'a, T, E> {
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
if state.bytes().starts_with(b"|>") || state.bytes().starts_with(b"||") {
Err((NoProgress, f(state.pos())))
} else {
p.parse(arena, state, min_indent)
}
}
}
mod when { mod when {
use parser::indented_seq_skip_first; use parser::indented_seq_skip_first;
@ -3005,6 +3074,11 @@ fn parse_stmt_seq<'a, E: SpaceProblem + 'a>(
break; break;
} }
if new_state.bytes().starts_with(b"|") {
state = state_before_space;
break;
}
return Err(( return Err((
MadeProgress, MadeProgress,
wrap_error(arena.alloc(EExpr::BadExprEnd(state.pos())), state.pos()), wrap_error(arena.alloc(EExpr::BadExprEnd(state.pos())), state.pos()),
@ -3209,7 +3283,7 @@ fn stmts_to_defs<'a>(
if (spaces_middle.len() <= 1 if (spaces_middle.len() <= 1
&& !ends_with_spaces_conservative(&ann_type.value) && !ends_with_spaces_conservative(&ann_type.value)
&& !starts_with_spaces_conservative(&loc_pattern.value)) && !starts_with_spaces_conservative(&loc_pattern.value))
|| header_to_pat(arena, header).equivalent(&loc_pattern.value) || type_header_equivalent_to_pat(&header, &loc_pattern.value)
{ {
// This is a case like // This is a case like
// UserId x : [UserId Int] // UserId x : [UserId Int]
@ -3351,18 +3425,33 @@ fn starts_with_spaces_conservative(value: &Pattern<'_>) -> bool {
Pattern::RequiredField(_, _) | Pattern::OptionalField(_, _) => false, Pattern::RequiredField(_, _) | Pattern::OptionalField(_, _) => false,
Pattern::SpaceBefore(_, _) => true, Pattern::SpaceBefore(_, _) => true,
Pattern::SpaceAfter(inner, _) => starts_with_spaces_conservative(inner), Pattern::SpaceAfter(inner, _) => starts_with_spaces_conservative(inner),
Pattern::Malformed(_) | Pattern::MalformedIdent(_, _) => true, Pattern::Malformed(_) | Pattern::MalformedIdent(_, _) | Pattern::MalformedExpr(_) => true,
} }
} }
fn header_to_pat<'a>(arena: &'a Bump, header: TypeHeader<'a>) -> Pattern<'a> { fn type_header_equivalent_to_pat<'a>(header: &TypeHeader<'a>, pat: &Pattern<'a>) -> bool {
if header.vars.is_empty() { match pat {
Pattern::Tag(header.name.value) Pattern::Apply(func, args) => {
} else { if !matches!(func.value, Pattern::Tag(tag) if header.name.value == tag) {
Pattern::Apply( return false;
arena.alloc(Loc::at(header.name.region, Pattern::Tag(header.name.value))), }
header.vars, if args.len() != header.vars.len() {
) return false;
}
for (arg, var) in (*args).iter().zip(header.vars) {
match (arg.value, var.value) {
(Pattern::Identifier { ident: left }, TypeVar::Identifier(right)) => {
if left != right {
return false;
}
}
_ => return false,
}
}
true
}
Pattern::Tag(tag) => header.vars.is_empty() && header.name.value == *tag,
_ => false,
} }
} }
@ -3375,7 +3464,7 @@ fn ends_with_spaces_conservative(ty: &TypeAnnotation<'_>) -> bool {
TypeAnnotation::As(_, _, type_header) => type_header TypeAnnotation::As(_, _, type_header) => type_header
.vars .vars
.last() .last()
.map_or(false, |v| pat_ends_with_spaces_conservative(&v.value)), .map_or(false, |v| type_var_ends_with_spaces_conservative(&v.value)),
TypeAnnotation::Record { fields: _, ext } TypeAnnotation::Record { fields: _, ext }
| TypeAnnotation::Tuple { elems: _, ext } | TypeAnnotation::Tuple { elems: _, ext }
| TypeAnnotation::TagUnion { ext, tags: _ } => { | TypeAnnotation::TagUnion { ext, tags: _ } => {
@ -3396,32 +3485,15 @@ fn ends_with_spaces_conservative(ty: &TypeAnnotation<'_>) -> bool {
} }
} }
fn pat_ends_with_spaces_conservative(pat: &Pattern<'_>) -> bool { fn type_var_ends_with_spaces_conservative(value: &TypeVar<'_>) -> bool {
match pat { match value {
Pattern::Identifier { .. } TypeVar::Identifier(_) => false,
| Pattern::QualifiedIdentifier { .. } TypeVar::Malformed(_) => {
| Pattern::Tag(_) // conservativly assume it might end in a space
| Pattern::NumLiteral(_) true
| Pattern::FloatLiteral(_) }
| Pattern::StrLiteral(_) TypeVar::SpaceBefore(inner, _sp) => type_var_ends_with_spaces_conservative(inner),
| Pattern::Underscore(_) TypeVar::SpaceAfter(_inner, _sp) => true,
| Pattern::SingleQuote(_)
| Pattern::Tuple(_)
| Pattern::List(_)
| Pattern::NonBase10Literal { .. }
| Pattern::ListRest(_)
| Pattern::As(_, _)
| Pattern::OpaqueRef(_)
| Pattern::PncApply(_, _) => false,
Pattern::Apply(_, args) => args
.last()
.map_or(false, |a| pat_ends_with_spaces_conservative(&a.value)),
Pattern::RecordDestructure(_) => false,
Pattern::RequiredField(_, _) => unreachable!(),
Pattern::OptionalField(_, _) => unreachable!(),
Pattern::SpaceBefore(inner, _) => pat_ends_with_spaces_conservative(inner),
Pattern::SpaceAfter(_, _) => true,
Pattern::Malformed(_) | Pattern::MalformedIdent(_, _) => false,
} }
} }
@ -3435,7 +3507,7 @@ pub fn join_alias_to_body<'a>(
body_expr: &'a Loc<Expr<'a>>, body_expr: &'a Loc<Expr<'a>>,
) -> ValueDef<'a> { ) -> ValueDef<'a> {
let loc_name = arena.alloc(header.name.map(|x| Pattern::Tag(x))); let loc_name = arena.alloc(header.name.map(|x| Pattern::Tag(x)));
let ann_pattern = Pattern::Apply(loc_name, header.vars); let ann_pattern = Pattern::Apply(loc_name, type_vars_to_patterns(arena, header.vars));
let vars_region = Region::across_all(header.vars.iter().map(|v| &v.region)); let vars_region = Region::across_all(header.vars.iter().map(|v| &v.region));
let region_ann_pattern = Region::span_across(&loc_name.region, &vars_region); let region_ann_pattern = Region::span_across(&loc_name.region, &vars_region);
@ -3450,6 +3522,35 @@ pub fn join_alias_to_body<'a>(
} }
} }
fn type_vars_to_patterns<'a>(
arena: &'a Bump,
vars: &'a [Loc<TypeVar<'a>>],
) -> &'a [Loc<Pattern<'a>>] {
let mut result = Vec::with_capacity_in(vars.len(), arena);
for var in vars {
let pat = type_var_to_pat(arena, &var.value);
result.push(Loc::at(var.region, pat));
}
result.into_bump_slice()
}
fn type_var_to_pat<'a>(arena: &'a Bump, var: &TypeVar<'a>) -> Pattern<'a> {
match var {
TypeVar::Identifier(ident) => Pattern::Identifier { ident },
TypeVar::Malformed(expr) => match expr_to_pattern_help(arena, expr) {
Ok(pat) => pat,
Err(()) => Pattern::MalformedExpr(expr),
},
TypeVar::SpaceBefore(inner, sp) => {
Pattern::SpaceBefore(arena.alloc(type_var_to_pat(arena, inner)), sp)
}
TypeVar::SpaceAfter(inner, sp) => {
Pattern::SpaceAfter(arena.alloc(type_var_to_pat(arena, inner)), sp)
}
}
}
/// This is a helper function for parsing function args. /// This is a helper function for parsing function args.
/// The rules for (-) are special-cased, and they come up in function args. /// The rules for (-) are special-cased, and they come up in function args.
/// ///
@ -4002,6 +4103,7 @@ where
good!(OperatorOrDef::BinOp(BinOp::Minus), 1) good!(OperatorOrDef::BinOp(BinOp::Minus), 1)
} }
"?" => good!(OperatorOrDef::BinOp(BinOp::SingleQuestion), 1),
"*" => good!(OperatorOrDef::BinOp(BinOp::Star), 1), "*" => good!(OperatorOrDef::BinOp(BinOp::Star), 1),
"/" => good!(OperatorOrDef::BinOp(BinOp::Slash), 1), "/" => good!(OperatorOrDef::BinOp(BinOp::Slash), 1),
"%" => good!(OperatorOrDef::BinOp(BinOp::Percent), 1), "%" => good!(OperatorOrDef::BinOp(BinOp::Percent), 1),

View file

@ -3,7 +3,7 @@ use bumpalo::Bump;
use roc_module::called_via::{BinOp, UnaryOp}; use roc_module::called_via::{BinOp, UnaryOp};
use roc_region::all::{Loc, Position, Region}; use roc_region::all::{Loc, Position, Region};
use crate::ast::ImplementsAbilities; use crate::ast::{ImplementsAbilities, TypeVar};
use crate::{ use crate::{
ast::{ ast::{
AbilityImpls, AbilityMember, AssignedField, Collection, Defs, Expr, FullAst, Header, AbilityImpls, AbilityMember, AssignedField, Collection, Defs, Expr, FullAst, Header,
@ -390,6 +390,18 @@ impl<'a> Normalize<'a> for TypeDef<'a> {
} }
} }
impl<'a> Normalize<'a> for TypeVar<'a> {
fn normalize(&self, arena: &'a Bump) -> Self {
match self {
TypeVar::Identifier(ident) => TypeVar::Identifier(ident),
TypeVar::Malformed(expr) => TypeVar::Malformed(expr.normalize(arena)),
TypeVar::SpaceBefore(inner, _sp) | TypeVar::SpaceAfter(inner, _sp) => {
*inner.normalize(arena)
}
}
}
}
impl<'a> Normalize<'a> for ValueDef<'a> { impl<'a> Normalize<'a> for ValueDef<'a> {
fn normalize(&self, arena: &'a Bump) -> Self { fn normalize(&self, arena: &'a Bump) -> Self {
use ValueDef::*; use ValueDef::*;
@ -919,6 +931,7 @@ impl<'a> Normalize<'a> for Pattern<'a> {
Pattern::StrLiteral(a) => Pattern::StrLiteral(a.normalize(arena)), Pattern::StrLiteral(a) => Pattern::StrLiteral(a.normalize(arena)),
Pattern::Underscore(a) => Pattern::Underscore(a), Pattern::Underscore(a) => Pattern::Underscore(a),
Pattern::Malformed(a) => Pattern::Malformed(a), Pattern::Malformed(a) => Pattern::Malformed(a),
Pattern::MalformedExpr(a) => Pattern::MalformedExpr(arena.alloc(a.normalize(arena))),
Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, remove_spaces_bad_ident(b)), Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, remove_spaces_bad_ident(b)),
Pattern::QualifiedIdentifier { module_name, ident } => { Pattern::QualifiedIdentifier { module_name, ident } => {
Pattern::QualifiedIdentifier { module_name, ident } Pattern::QualifiedIdentifier { module_name, ident }
@ -1201,6 +1214,7 @@ impl<'a> Normalize<'a> for EClosure<'a> {
EClosure::IndentArrow(_) => EClosure::IndentArrow(Position::zero()), EClosure::IndentArrow(_) => EClosure::IndentArrow(Position::zero()),
EClosure::IndentBody(_) => EClosure::IndentBody(Position::zero()), EClosure::IndentBody(_) => EClosure::IndentBody(Position::zero()),
EClosure::IndentArg(_) => EClosure::IndentArg(Position::zero()), EClosure::IndentArg(_) => EClosure::IndentArg(Position::zero()),
EClosure::Bar(_) => EClosure::Bar(Position::zero()),
} }
} }
} }

View file

@ -747,6 +747,7 @@ impl<'a> EInParens<'a> {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum EClosure<'a> { pub enum EClosure<'a> {
Bar(Position),
Space(BadInputError, Position), Space(BadInputError, Position),
Start(Position), Start(Position),
Arrow(Position), Arrow(Position),
@ -768,7 +769,8 @@ impl<'a> EClosure<'a> {
EClosure::Body(expr, _) => expr.get_region(), EClosure::Body(expr, _) => expr.get_region(),
// Cases with Position values // Cases with Position values
EClosure::Space(_, p) EClosure::Bar(p)
| EClosure::Space(_, p)
| EClosure::Start(p) | EClosure::Start(p)
| EClosure::Arrow(p) | EClosure::Arrow(p)
| EClosure::Comma(p) | EClosure::Comma(p)

View file

@ -1,7 +1,7 @@
use crate::ast::{ use crate::ast::{
AbilityImpls, AssignedField, Collection, CommentOrNewline, Expr, FunctionArrow, AbilityImpls, AssignedField, Collection, CommentOrNewline, Expr, FunctionArrow,
ImplementsAbilities, ImplementsAbility, ImplementsClause, Pattern, Spaceable, Spaced, ImplementsAbilities, ImplementsAbility, ImplementsClause, Spaceable, Spaced, SpacesBefore, Tag,
SpacesBefore, Tag, TypeAnnotation, TypeHeader, TypeAnnotation, TypeHeader, TypeVar,
}; };
use crate::blankspace::{ use crate::blankspace::{
self, plain_spaces_before, space0_around_ee, space0_before, space0_before_e, space0_e, self, plain_spaces_before, space0_around_ee, space0_before, space0_before_e, space0_e,
@ -74,7 +74,7 @@ fn check_type_alias<'a>(
var_names.reserve(vars.len()); var_names.reserve(vars.len());
for var in vars { for var in vars {
if let TypeAnnotation::BoundVariable(v) = var.value { if let TypeAnnotation::BoundVariable(v) = var.value {
var_names.push(Loc::at(var.region, Pattern::Identifier { ident: v })); var_names.push(Loc::at(var.region, TypeVar::Identifier(v)));
} else { } else {
return Err(ETypeInlineAlias::ArgumentNotLowercase(var.region.start())); return Err(ETypeInlineAlias::ArgumentNotLowercase(var.region.start()));
} }

View file

@ -3479,7 +3479,7 @@ mod solve_expr {
Ok { position, cost: 0.0 } Ok { position, cost: 0.0 }
Set.walk model.open_set (Ok { position: boom {}, cost: 0.0 }) folder Set.walk model.open_set (Ok { position: boom {}, cost: 0.0 }) folder
|> Result.map (\x -> x.position) |> Result.map_ok (\x -> x.position)
astar : Model position -> Result position [KeyNotFound] where position implements Hash & Eq astar : Model position -> Result position [KeyNotFound] where position implements Hash & Eq
astar = \model -> cheapest_open model astar = \model -> cheapest_open model

View file

@ -337,7 +337,7 @@ fn decode() {
# impl MDecoding for MyU8 # impl MDecoding for MyU8
decoder = @MDecoder \lst, fmt -> decoder = @MDecoder \lst, fmt ->
{ result, rest } = decode_with lst u8 fmt { result, rest } = decode_with lst u8 fmt
{ result: Result.map result (\n -> @MyU8 n), rest } { result: Result.map_ok result (\n -> @MyU8 n), rest }
myU8 = myU8 =
when from_bytes [15] (@Linear {}) is when from_bytes [15] (@Linear {}) is
@ -2210,7 +2210,7 @@ fn issue_4772_weakened_monomorphic_destructure() {
Err (ParsingFailure "not a number") Err (ParsingFailure "not a number")
main = main =
get_number |> Result.map .val |> Result.with_default 0 get_number |> Result.map_ok .val |> Result.with_default 0
"# "#
), ),
1234i64, 1234i64,

View file

@ -554,7 +554,7 @@ fn list_split_first() {
assert_evals_to!( assert_evals_to!(
r" r"
List.split_first [2, 3, 0, 4, 0, 6, 0, 8, 9] 0 List.split_first [2, 3, 0, 4, 0, 6, 0, 8, 9] 0
|> Result.map .before |> Result.map_ok .before
", ",
RocResult::ok(RocList::<i64>::from_slice(&[2, 3])), RocResult::ok(RocList::<i64>::from_slice(&[2, 3])),
RocResult<RocList<i64>, ()> RocResult<RocList<i64>, ()>
@ -562,7 +562,7 @@ fn list_split_first() {
assert_evals_to!( assert_evals_to!(
r" r"
List.split_first [2, 3, 0, 4, 0, 6, 0, 8, 9] 0 List.split_first [2, 3, 0, 4, 0, 6, 0, 8, 9] 0
|> Result.map .after |> Result.map_ok .after
", ",
RocResult::ok(RocList::<i64>::from_slice(&[4, 0, 6, 0, 8, 9])), RocResult::ok(RocList::<i64>::from_slice(&[4, 0, 6, 0, 8, 9])),
RocResult<RocList<i64>, ()> RocResult<RocList<i64>, ()>
@ -587,7 +587,7 @@ fn list_split_last() {
assert_evals_to!( assert_evals_to!(
r" r"
List.split_last [2, 3, 0, 4, 0, 6, 0, 8, 9] 0 List.split_last [2, 3, 0, 4, 0, 6, 0, 8, 9] 0
|> Result.map .before |> Result.map_ok .before
", ",
RocResult::ok(RocList::<i64>::from_slice(&[2, 3, 0, 4, 0, 6])), RocResult::ok(RocList::<i64>::from_slice(&[2, 3, 0, 4, 0, 6])),
RocResult<RocList<i64>, ()> RocResult<RocList<i64>, ()>
@ -595,7 +595,7 @@ fn list_split_last() {
assert_evals_to!( assert_evals_to!(
r" r"
List.split_last [2, 3, 0, 4, 0, 6, 0, 8, 9] 0 List.split_last [2, 3, 0, 4, 0, 6, 0, 8, 9] 0
|> Result.map .after |> Result.map_ok .after
", ",
RocResult::ok(RocList::<i64>::from_slice(&[8, 9])), RocResult::ok(RocList::<i64>::from_slice(&[8, 9])),
RocResult<RocList<i64>, ()> RocResult<RocList<i64>, ()>
@ -2164,7 +2164,7 @@ fn first_wildcard_empty_list() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r" r"
List.last [] |> Result.map (\_ -> 0i64) List.last [] |> Result.map_ok (\_ -> 0i64)
" "
), ),
RocResult::err(()), RocResult::err(()),
@ -2209,7 +2209,7 @@ fn last_wildcard_empty_list() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r" r"
List.last [] |> Result.map (\_ -> 0i64) List.last [] |> Result.map_ok (\_ -> 0i64)
" "
), ),
RocResult::err(()), RocResult::err(()),
@ -2261,7 +2261,7 @@ fn get_wildcard_empty_list() {
indoc!( indoc!(
r" r"
List.get [] 0 List.get [] 0
|> Result.map (\_ -> {}) |> Result.map_ok (\_ -> {})
" "
), ),
RocResult::err(()), RocResult::err(()),
@ -2978,7 +2978,7 @@ fn list_min() {
indoc!( indoc!(
r" r"
List.min [] List.min []
|> Result.map (\_ -> {}) |> Result.map_ok (\_ -> {})
" "
), ),
RocResult::err(()), RocResult::err(()),
@ -3003,7 +3003,7 @@ fn list_max() {
indoc!( indoc!(
r" r"
List.max [] List.max []
|> Result.map (\_ -> {}) |> Result.map_ok (\_ -> {})
" "
), ),
RocResult::err(()), RocResult::err(()),

View file

@ -56,7 +56,7 @@ fn result_map() {
result = Ok 2 result = Ok 2
result result
|> Result.map (\x -> x + 1) |> Result.map_ok (\x -> x + 1)
|> Result.with_default 0 |> Result.with_default 0
" "
), ),
@ -71,7 +71,7 @@ fn result_map() {
result = Err {} result = Err {}
result result
|> Result.map (\x -> x + 1) |> Result.map_ok (\x -> x + 1)
|> Result.with_default 0 |> Result.with_default 0
" "
), ),
@ -120,7 +120,7 @@ fn err_type_var() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r" r"
Result.map (Ok 3) (\x -> x + 1) Result.map_ok (Ok 3) (\x -> x + 1)
|> Result.with_default -1 |> Result.with_default -1
" "
), ),
@ -138,7 +138,7 @@ fn err_type_var_annotation() {
ok : Result I64 * ok : Result I64 *
ok = Ok 3 ok = Ok 3
Result.map ok (\x -> x + 1) Result.map_ok ok (\x -> x + 1)
|> Result.with_default -1 |> Result.with_default -1
" "
), ),
@ -156,7 +156,7 @@ fn err_empty_tag_union() {
ok : Result I64 [] ok : Result I64 []
ok = Ok 3 ok = Ok 3
Result.map ok (\x -> x + 1) Result.map_ok ok (\x -> x + 1)
|> Result.with_default -1 |> Result.with_default -1
" "
), ),

View file

@ -30,7 +30,7 @@ procedure Test.2 (Test.6):
let Test.9 : List [<r>C List [<r>C List *self, C *self], C [<r>C List *self, C *self]] = UnionAtIndex (Id 0) (Index 0) Test.6; let Test.9 : List [<r>C List [<r>C List *self, C *self], C [<r>C List *self, C *self]] = UnionAtIndex (Id 0) (Index 0) Test.6;
joinpoint #Derived_gen.3: joinpoint #Derived_gen.3:
dec Test.9; dec Test.9;
let Test.21 : Str = "ValueNotExposed { module_name: ModuleName(IdentStr { string: \"Result\" }), ident: Ident(IdentStr { string: \"withDefault\" }), region: @662-680, exposed_values: ['is_err', 'on_err', 'map', 'map_err', 'with_default', 'try', 'is_ok', 'map_both', 'map2', 'on_err!'] }"; let Test.21 : Str = "ValueNotExposed { module_name: ModuleName(IdentStr { string: \"Result\" }), ident: Ident(IdentStr { string: \"withDefault\" }), region: @662-680, exposed_values: ['is_err', 'on_err', 'map_ok', 'map_err', 'with_default', 'try', 'is_ok', 'map_both', 'map2', 'on_err!'] }";
Crash Test.21 Crash Test.21
in in
let #Derived_gen.4 : Int1 = lowlevel RefCountIsUnique Test.6; let #Derived_gen.4 : Int1 = lowlevel RefCountIsUnique Test.6;

View file

@ -1 +1 @@
Expr(Pattern(Start(@4), @4), @0) Expr(Ability(DemandAlignment(-1, @18), @17), @0)

View file

@ -0,0 +1,10 @@
O {
p?? if
a
then
A
else
&m,
} #
: e
i

View file

@ -20,10 +20,12 @@
name: @0-1 "O", name: @0-1 "O",
vars: [ vars: [
@1-23 SpaceAfter( @1-23 SpaceAfter(
RecordDestructure( Malformed(
Record(
[ [
@2-22 OptionalField( @2-22 OptionalValue(
"p", @2-3 "p",
[],
@4-22 If { @4-22 If {
if_thens: [ if_thens: [
( (
@ -64,6 +66,7 @@
), ),
], ],
), ),
),
[ [
LineComment( LineComment(
"", "",

View file

@ -19,9 +19,11 @@
header: TypeHeader { header: TypeHeader {
name: @0-1 "C", name: @0-1 "C",
vars: [ vars: [
@2-3 NumLiteral( @2-3 Malformed(
Num(
"4", "4",
), ),
),
], ],
}, },
loc_implements: @7-17 SpaceBefore( loc_implements: @7-17 SpaceBefore(

View file

@ -0,0 +1,2 @@
N (implements) h (0) : B
T

View file

@ -19,15 +19,24 @@
header: TypeHeader { header: TypeHeader {
name: @0-1 "N", name: @0-1 "N",
vars: [ vars: [
@3-13 Identifier { @3-13 Malformed(
ParensAround(
Var {
module_name: "",
ident: "implements", ident: "implements",
}, },
@15-16 Identifier { ),
ident: "h", ),
}, @15-16 Identifier(
@18-19 NumLiteral( "h",
),
@18-19 Malformed(
ParensAround(
Num(
"0", "0",
), ),
),
),
], ],
}, },
ann: @21-22 Apply( ann: @21-22 Apply(

View file

@ -19,12 +19,16 @@
header: TypeHeader { header: TypeHeader {
name: @0-1 "N", name: @0-1 "N",
vars: [ vars: [
@2-4 NumLiteral( @2-4 Malformed(
Num(
"-0", "-0",
), ),
@5-6 Tag( ),
@5-6 Malformed(
Tag(
"T", "T",
), ),
),
], ],
}, },
ann: @7-8 Apply( ann: @7-8 Apply(

View file

@ -28,9 +28,11 @@
header: TypeHeader { header: TypeHeader {
name: @0-1 "B", name: @0-1 "B",
vars: [ vars: [
@1-3 OpaqueRef( @1-3 Malformed(
OpaqueRef(
"@A", "@A",
), ),
),
], ],
}, },
ann: @4-5 BoundVariable( ann: @4-5 BoundVariable(

View file

@ -19,8 +19,11 @@
header: TypeHeader { header: TypeHeader {
name: @0-2 "Zx", name: @0-2 "Zx",
vars: [ vars: [
@4-5 SpaceAfter( @4-5 Malformed(
Identifier { ParensAround(
SpaceAfter(
Var {
module_name: "",
ident: "e", ident: "e",
}, },
[ [
@ -29,9 +32,11 @@
), ),
], ],
), ),
@8-9 Identifier { ),
ident: "f", ),
}, @8-9 Identifier(
"f",
),
], ],
}, },
ann: @10-11 BoundVariable( ann: @10-11 BoundVariable(

View file

@ -19,15 +19,22 @@
header: TypeHeader { header: TypeHeader {
name: @0-1 "U", name: @0-1 "U",
vars: [ vars: [
@3-6 Apply( @3-6 Malformed(
@3-4 Identifier { ParensAround(
Apply(
@3-4 Var {
module_name: "",
ident: "b", ident: "b",
}, },
[ [
@5-6 Identifier { @5-6 Var {
module_name: "",
ident: "a", ident: "a",
}, },
], ],
Space,
),
),
), ),
], ],
}, },

View file

@ -0,0 +1,10 @@
M {
s?? s
{ J &
},
} {
s?? s
{ J &
},
} : p
y

View file

@ -0,0 +1,104 @@
@0-25 SpaceAfter(
Defs(
Defs {
tags: [
EitherIndex(0),
],
regions: [
@0-23,
],
space_before: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
space_after: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
spaces: [],
type_defs: [
Alias {
header: TypeHeader {
name: @0-1 "M",
vars: [
@1-11 Malformed(
Record(
[
@2-10 OptionalValue(
@2-3 "s",
[],
@4-10 Apply(
@4-5 Var {
module_name: "",
ident: "s",
},
[
@5-10 RecordUpdate {
update: @6-7 Tag(
"J",
),
fields: Collection {
items: [],
final_comments: [
Newline,
],
},
},
],
Space,
),
),
],
),
),
@11-21 Malformed(
Record(
[
@12-20 OptionalValue(
@12-13 "s",
[],
@14-20 Apply(
@14-15 Var {
module_name: "",
ident: "s",
},
[
@15-20 RecordUpdate {
update: @16-17 Tag(
"J",
),
fields: Collection {
items: [],
final_comments: [
Newline,
],
},
},
],
Space,
),
),
],
),
),
],
},
ann: @22-23 BoundVariable(
"p",
),
},
],
value_defs: [],
},
@24-25 SpaceBefore(
Var {
module_name: "",
ident: "y",
},
[
Newline,
],
),
),
[
Newline,
],
)

View file

@ -0,0 +1,5 @@
Q (
"""
"""
"") : a
q

View file

@ -19,19 +19,24 @@
header: TypeHeader { header: TypeHeader {
name: @0-1 "Q", name: @0-1 "Q",
vars: [ vars: [
@3-11 Apply( @3-11 Malformed(
@3-9 StrLiteral( ParensAround(
Apply(
@3-9 Str(
Block( Block(
[], [],
), ),
), ),
[ [
@9-11 StrLiteral( @9-11 Str(
PlainLine( PlainLine(
"", "",
), ),
), ),
], ],
Space,
),
),
), ),
], ],
}, },

View file

@ -0,0 +1,4 @@
il3 |k| # w#z
CCC @C ( # i
t! K) : i
C

View file

@ -32,12 +32,17 @@
header: TypeHeader { header: TypeHeader {
name: @13-16 "CCC", name: @13-16 "CCC",
vars: [ vars: [
@16-18 OpaqueRef( @16-18 Malformed(
OpaqueRef(
"@C", "@C",
), ),
@23-26 SpaceBefore( ),
@23-26 Malformed(
ParensAround(
SpaceBefore(
Apply( Apply(
@23-25 Identifier { @23-25 Var {
module_name: "",
ident: "t!", ident: "t!",
}, },
[ [
@ -45,6 +50,7 @@
"K", "K",
), ),
], ],
Space,
), ),
[ [
LineComment( LineComment(
@ -52,6 +58,8 @@
), ),
], ],
), ),
),
),
], ],
}, },
ann: @28-29 BoundVariable( ann: @28-29 BoundVariable(

View file

@ -19,9 +19,9 @@
name: @0-1 "A", name: @0-1 "A",
vars: [ vars: [
@4-5 SpaceBefore( @4-5 SpaceBefore(
Identifier { Identifier(
ident: "p", "p",
}, ),
[ [
LineComment( LineComment(
"", "",

View file

@ -1,5 +1,5 @@
launchTheNukes : {} => Result Bool LaunchNukeErr launchTheNukes : {} => Result Bool LaunchNukeErr
launchTheNukes = \{} -> launchTheNukes = |{}|
crash "todo" crash "todo"
launchTheNukes launchTheNukes

View file

@ -19,9 +19,9 @@
name: @0-1 "A", name: @0-1 "A",
vars: [ vars: [
@3-4 SpaceBefore( @3-4 SpaceBefore(
Identifier { Identifier(
ident: "p", "p",
}, ),
[ [
Newline, Newline,
], ],

View file

@ -20,9 +20,9 @@
vars: [ vars: [
@3-4 SpaceAfter( @3-4 SpaceAfter(
SpaceBefore( SpaceBefore(
Identifier { Identifier(
ident: "e", "e",
}, ),
[ [
Newline, Newline,
], ],

View file

@ -1,2 +1,2 @@
foo foo
|> Dict.keepIf \(k, _v) -> List.contains keysToDelete k |> Bool.not |> Dict.keepIf |(k, _v)| List.contains keysToDelete k |> Bool.not

View file

@ -1,2 +1,2 @@
\({ x, y } as point), (@Location inner as outer) -> |({ x, y } as point), (@Location inner as outer)|
crash "" crash ""

View file

@ -1,4 +1,4 @@
d d
+ +
(\w -> x) (|w| x)
x x

View file

@ -1,3 +1,3 @@
\I { p ?? Y |I { p ?? Y
Y } [] -> Y } []|
K # ( K # (

View file

@ -1,2 +1,2 @@
m0 \w -> w? e m0 |w| w? e
/ s / s

View file

@ -1,3 +1,3 @@
m m
^ (-(\w -> m)) ^ (-(|w| m))
w w

View file

@ -19,9 +19,9 @@
header: TypeHeader { header: TypeHeader {
name: @0-5 "Model", name: @0-5 "Model",
vars: [ vars: [
@6-14 Identifier { @6-14 Identifier(
ident: "position", "position",
}, ),
], ],
}, },
ann: @21-164 SpaceBefore( ann: @21-164 SpaceBefore(

View file

@ -1,7 +1,7 @@
_ = crash "" _ = crash ""
_ = crash "" "" _ = crash "" ""
_ = crash 15 123 _ = crash 15 123
_ = try foo (\_ -> crash "") _ = try foo (|_| crash "")
_ = _ =
_ = crash "" _ = crash ""
crash crash

View file

@ -1,4 +1,4 @@
launchTheNukes! = \{} -> launchTheNukes! = |{}|
boom boom
launchTheNukes! {} launchTheNukes! {}

View file

@ -1,3 +1,3 @@
\j -> |j|
e \B -> B e |B| B
> s > s

View file

@ -1,4 +1,4 @@
\{} -> |{}|
echo "Welcome to the DMV!" echo "Welcome to the DMV!"
age = readInt age = readInt

View file

@ -3,5 +3,5 @@ table :
height : Pixels, height : Pixels,
} }
-> Table -> Table
table = \{ height } -> crash "not implemented" table = |{ height }| crash "not implemented"
table table

View file

@ -1,4 +1,4 @@
f : (Str)a -> (Str)a f : (Str)a -> (Str)a
f = \x -> x f = |x| x
f ("Str", 42) f ("Str", 42)

Some files were not shown because too many files have changed in this diff Show more