diff --git a/.editorconfig b/.editorconfig index 2c102485..4bb97b19 100644 --- a/.editorconfig +++ b/.editorconfig @@ -18,6 +18,5 @@ indent_size = 2 [*.erl] indent_size = 4 -[*.{gleam, erl, mjs, js, ts}] +[*.{erl, mjs, js, ts}] max_line_length = 80 - diff --git a/src/gleam/bit_array.gleam b/src/gleam/bit_array.gleam index 14db9027..2c70d145 100644 --- a/src/gleam/bit_array.gleam +++ b/src/gleam/bit_array.gleam @@ -48,20 +48,20 @@ pub fn slice( /// Tests to see whether a bit array is valid UTF-8. /// pub fn is_utf8(bits: BitArray) -> Bool { - do_is_utf8(bits) + is_utf8_loop(bits) } @target(erlang) -fn do_is_utf8(bits: BitArray) -> Bool { +fn is_utf8_loop(bits: BitArray) -> Bool { case bits { <<>> -> True - <<_:utf8, rest:bytes>> -> do_is_utf8(rest) + <<_:utf8, rest:bytes>> -> is_utf8_loop(rest) _ -> False } } @target(javascript) -fn do_is_utf8(bits: BitArray) -> Bool { +fn is_utf8_loop(bits: BitArray) -> Bool { case to_string(bits) { Ok(_) -> True _ -> False @@ -158,11 +158,11 @@ pub fn base16_decode(input: String) -> Result(BitArray, Nil) /// ``` /// pub fn inspect(input: BitArray) -> String { - do_inspect(input, "<<") <> ">>" + inspect_loop(input, "<<") <> ">>" } @external(javascript, "../gleam_stdlib.mjs", "bit_array_inspect") -fn do_inspect(input: BitArray, accumulator: String) -> String { +fn inspect_loop(input: BitArray, accumulator: String) -> String { case input { <<>> -> accumulator @@ -181,8 +181,7 @@ fn do_inspect(input: BitArray, accumulator: String) -> String { } let accumulator = accumulator <> int.to_string(x) <> suffix - - do_inspect(rest, accumulator) + inspect_loop(rest, accumulator) } _ -> accumulator diff --git a/src/gleam/dict.gleam b/src/gleam/dict.gleam index 10b55445..4697c8d7 100644 --- a/src/gleam/dict.gleam +++ b/src/gleam/dict.gleam @@ -223,7 +223,7 @@ pub fn keys(dict: Dict(k, v)) -> List(k) { @external(erlang, "maps", "keys") fn do_keys(dict: Dict(k, v)) -> List(k) { let list_of_pairs = to_list(dict) - do_keys_acc(list_of_pairs, []) + do_keys_loop(list_of_pairs, []) } fn reverse_and_concat(remaining: List(a), accumulator: List(a)) -> List(a) { @@ -233,10 +233,10 @@ fn reverse_and_concat(remaining: List(a), accumulator: List(a)) -> List(a) { } } -fn do_keys_acc(list: List(#(k, v)), acc: List(k)) -> List(k) { +fn do_keys_loop(list: List(#(k, v)), acc: List(k)) -> List(k) { case list { [] -> reverse_and_concat(acc, []) - [first, ..rest] -> do_keys_acc(rest, [first.0, ..acc]) + [first, ..rest] -> do_keys_loop(rest, [first.0, ..acc]) } } @@ -260,13 +260,13 @@ pub fn values(dict: Dict(k, v)) -> List(v) { @external(erlang, "maps", "values") fn do_values(dict: Dict(k, v)) -> List(v) { let list_of_pairs = to_list(dict) - do_values_acc(list_of_pairs, []) + do_values_loop(list_of_pairs, []) } -fn do_values_acc(list: List(#(k, v)), acc: List(v)) -> List(v) { +fn do_values_loop(list: List(#(k, v)), acc: List(v)) -> List(v) { case list { [] -> reverse_and_concat(acc, []) - [first, ..rest] -> do_values_acc(rest, [first.1, ..acc]) + [first, ..rest] -> do_values_loop(rest, [first.1, ..acc]) } } @@ -302,8 +302,8 @@ fn do_filter(f: fn(k, v) -> Bool, dict: Dict(k, v)) -> Dict(k, v) { _ -> dict } } - dict - |> fold(from: new(), with: insert) + + fold(dict, from: new(), with: insert) } /// Creates a new dict from a given dict, only including any entries for which the @@ -329,10 +329,10 @@ pub fn take(from dict: Dict(k, v), keeping desired_keys: List(k)) -> Dict(k, v) @external(erlang, "maps", "with") fn do_take(desired_keys: List(k), dict: Dict(k, v)) -> Dict(k, v) { - insert_taken(dict, desired_keys, new()) + do_take_loop(dict, desired_keys, new()) } -fn insert_taken( +fn do_take_loop( dict: Dict(k, v), desired_keys: List(k), acc: Dict(k, v), @@ -345,7 +345,7 @@ fn insert_taken( } case desired_keys { [] -> acc - [first, ..rest] -> insert_taken(dict, rest, insert(acc, first)) + [first, ..rest] -> do_take_loop(dict, rest, insert(acc, first)) } } @@ -470,13 +470,6 @@ pub fn upsert( |> insert(dict, key, _) } -fn do_fold(list: List(#(k, v)), initial: acc, fun: fn(acc, k, v) -> acc) -> acc { - case list { - [] -> initial - [#(k, v), ..rest] -> do_fold(rest, fun(initial, k, v), fun) - } -} - /// Combines all entries into a single value by calling a given function on each /// one. /// @@ -507,9 +500,18 @@ pub fn fold( from initial: acc, with fun: fn(acc, k, v) -> acc, ) -> acc { - dict - |> to_list - |> do_fold(initial, fun) + fold_loop(to_list(dict), initial, fun) +} + +fn fold_loop( + list: List(#(k, v)), + initial: acc, + fun: fn(acc, k, v) -> acc, +) -> acc { + case list { + [] -> initial + [#(k, v), ..rest] -> fold_loop(rest, fun(initial, k, v), fun) + } } /// Calls a function for each key and value in a dict, discarding the return diff --git a/src/gleam/float.gleam b/src/gleam/float.gleam index 7030caa3..a7d6dc8c 100644 --- a/src/gleam/float.gleam +++ b/src/gleam/float.gleam @@ -403,14 +403,13 @@ pub fn negate(x: Float) -> Float { /// ``` /// pub fn sum(numbers: List(Float)) -> Float { - numbers - |> do_sum(0.0) + sum_loop(numbers, 0.0) } -fn do_sum(numbers: List(Float), initial: Float) -> Float { +fn sum_loop(numbers: List(Float), initial: Float) -> Float { case numbers { + [x, ..rest] -> sum_loop(rest, x +. initial) [] -> initial - [x, ..rest] -> do_sum(rest, x +. initial) } } @@ -426,14 +425,14 @@ fn do_sum(numbers: List(Float), initial: Float) -> Float { pub fn product(numbers: List(Float)) -> Float { case numbers { [] -> 1.0 - _ -> do_product(numbers, 1.0) + _ -> product_loop(numbers, 1.0) } } -fn do_product(numbers: List(Float), initial: Float) -> Float { +fn product_loop(numbers: List(Float), initial: Float) -> Float { case numbers { + [x, ..rest] -> product_loop(rest, x *. initial) [] -> initial - [x, ..rest] -> do_product(rest, x *. initial) } } diff --git a/src/gleam/int.gleam b/src/gleam/int.gleam index 5d60b6ec..de039d50 100644 --- a/src/gleam/int.gleam +++ b/src/gleam/int.gleam @@ -427,14 +427,13 @@ pub fn negate(x: Int) -> Int { /// ``` /// pub fn sum(numbers: List(Int)) -> Int { - numbers - |> do_sum(0) + sum_loop(numbers, 0) } -fn do_sum(numbers: List(Int), initial: Int) -> Int { +fn sum_loop(numbers: List(Int), initial: Int) -> Int { case numbers { + [x, ..rest] -> sum_loop(rest, x + initial) [] -> initial - [x, ..rest] -> do_sum(rest, x + initial) } } @@ -450,14 +449,14 @@ fn do_sum(numbers: List(Int), initial: Int) -> Int { pub fn product(numbers: List(Int)) -> Int { case numbers { [] -> 1 - _ -> do_product(numbers, 1) + _ -> product_loop(numbers, 1) } } -fn do_product(numbers: List(Int), initial: Int) -> Int { +fn product_loop(numbers: List(Int), initial: Int) -> Int { case numbers { + [x, ..rest] -> product_loop(rest, x * initial) [] -> initial - [x, ..rest] -> do_product(rest, x * initial) } } @@ -479,14 +478,14 @@ fn do_product(numbers: List(Int), initial: Int) -> Int { pub fn digits(x: Int, base: Int) -> Result(List(Int), Nil) { case base < 2 { True -> Error(Nil) - False -> Ok(do_digits(x, base, [])) + False -> Ok(digits_loop(x, base, [])) } } -fn do_digits(x: Int, base: Int, acc: List(Int)) -> List(Int) { +fn digits_loop(x: Int, base: Int, acc: List(Int)) -> List(Int) { case absolute_value(x) < base { True -> [x, ..acc] - False -> do_digits(x / base, base, [x % base, ..acc]) + False -> digits_loop(x / base, base, [x % base, ..acc]) } } @@ -513,15 +512,15 @@ fn do_digits(x: Int, base: Int, acc: List(Int)) -> List(Int) { pub fn undigits(numbers: List(Int), base: Int) -> Result(Int, Nil) { case base < 2 { True -> Error(Nil) - False -> do_undigits(numbers, base, 0) + False -> undigits_loop(numbers, base, 0) } } -fn do_undigits(numbers: List(Int), base: Int, acc: Int) -> Result(Int, Nil) { +fn undigits_loop(numbers: List(Int), base: Int, acc: Int) -> Result(Int, Nil) { case numbers { [] -> Ok(acc) [digit, ..] if digit >= base -> Error(Nil) - [digit, ..rest] -> do_undigits(rest, base, acc * base + digit) + [digit, ..rest] -> undigits_loop(rest, base, acc * base + digit) } } diff --git a/src/gleam/iterator.gleam b/src/gleam/iterator.gleam index 3232a12f..812436be 100644 --- a/src/gleam/iterator.gleam +++ b/src/gleam/iterator.gleam @@ -38,19 +38,6 @@ fn stop() -> Action(element) { Stop } -// Creating Iterators -fn do_unfold( - initial: acc, - f: fn(acc) -> Step(element, acc), -) -> fn() -> Action(element) { - fn() { - case f(initial) { - Next(x, acc) -> Continue(x, do_unfold(acc, f)) - Done -> Stop - } - } -} - /// Creates an iterator from a given function and accumulator. /// /// The function is called on the accumulator and returns either `Done`, @@ -77,10 +64,23 @@ pub fn unfold( with f: fn(acc) -> Step(element, acc), ) -> Iterator(element) { initial - |> do_unfold(f) + |> unfold_loop(f) |> Iterator } +// Creating Iterators +fn unfold_loop( + initial: acc, + f: fn(acc) -> Step(element, acc), +) -> fn() -> Action(element) { + fn() { + case f(initial) { + Next(x, acc) -> Continue(x, unfold_loop(acc, f)) + Done -> Stop + } + } +} + /// Creates an iterator that yields values created by calling a given function /// repeatedly. /// @@ -131,7 +131,7 @@ pub fn from_list(list: List(element)) -> Iterator(element) { } // Consuming Iterators -fn do_transform( +fn transform_loop( continuation: fn() -> Action(a), state: acc, f: fn(acc, a) -> Step(b, acc), @@ -143,7 +143,7 @@ fn do_transform( case f(state, el) { Done -> Stop Next(yield, next_state) -> - Continue(yield, do_transform(next, next_state, f)) + Continue(yield, transform_loop(next, next_state, f)) } } } @@ -171,17 +171,17 @@ pub fn transform( from initial: acc, with f: fn(acc, a) -> Step(b, acc), ) -> Iterator(b) { - do_transform(iterator.continuation, initial, f) + transform_loop(iterator.continuation, initial, f) |> Iterator } -fn do_fold( +fn fold_loop( continuation: fn() -> Action(e), f: fn(acc, e) -> acc, accumulator: acc, ) -> acc { case continuation() { - Continue(elem, next) -> do_fold(next, f, f(accumulator, elem)) + Continue(elem, next) -> fold_loop(next, f, f(accumulator, elem)) Stop -> accumulator } } @@ -209,7 +209,7 @@ pub fn fold( with f: fn(acc, e) -> acc, ) -> acc { iterator.continuation - |> do_fold(f, initial) + |> fold_loop(f, initial) } // TODO: test @@ -270,19 +270,6 @@ pub fn step(iterator: Iterator(e)) -> Step(e, Iterator(e)) { } } -fn do_take(continuation: fn() -> Action(e), desired: Int) -> fn() -> Action(e) { - fn() { - case desired > 0 { - False -> Stop - True -> - case continuation() { - Stop -> Stop - Continue(e, next) -> Continue(e, do_take(next, desired - 1)) - } - } - } -} - /// Creates an iterator that only yields the first `desired` elements. /// /// If the iterator does not have enough elements all of them are yielded. @@ -305,18 +292,20 @@ fn do_take(continuation: fn() -> Action(e), desired: Int) -> fn() -> Action(e) { /// pub fn take(from iterator: Iterator(e), up_to desired: Int) -> Iterator(e) { iterator.continuation - |> do_take(desired) + |> take_loop(desired) |> Iterator } -fn do_drop(continuation: fn() -> Action(e), desired: Int) -> Action(e) { - case continuation() { - Stop -> Stop - Continue(e, next) -> - case desired > 0 { - True -> do_drop(next, desired - 1) - False -> Continue(e, next) - } +fn take_loop(continuation: fn() -> Action(e), desired: Int) -> fn() -> Action(e) { + fn() { + case desired > 0 { + False -> Stop + True -> + case continuation() { + Stop -> Stop + Continue(e, next) -> Continue(e, take_loop(next, desired - 1)) + } + } } } @@ -346,16 +335,18 @@ fn do_drop(continuation: fn() -> Action(e), desired: Int) -> Action(e) { /// ``` /// pub fn drop(from iterator: Iterator(e), up_to desired: Int) -> Iterator(e) { - fn() { do_drop(iterator.continuation, desired) } + fn() { drop_loop(iterator.continuation, desired) } |> Iterator } -fn do_map(continuation: fn() -> Action(a), f: fn(a) -> b) -> fn() -> Action(b) { - fn() { - case continuation() { - Stop -> Stop - Continue(e, continuation) -> Continue(f(e), do_map(continuation, f)) - } +fn drop_loop(continuation: fn() -> Action(e), desired: Int) -> Action(e) { + case continuation() { + Stop -> Stop + Continue(e, next) -> + case desired > 0 { + True -> drop_loop(next, desired - 1) + False -> Continue(e, next) + } } } @@ -378,24 +369,15 @@ fn do_map(continuation: fn() -> Action(a), f: fn(a) -> b) -> fn() -> Action(b) { /// pub fn map(over iterator: Iterator(a), with f: fn(a) -> b) -> Iterator(b) { iterator.continuation - |> do_map(f) + |> map_loop(f) |> Iterator } -fn do_map2( - continuation1: fn() -> Action(a), - continuation2: fn() -> Action(b), - with fun: fn(a, b) -> c, -) -> fn() -> Action(c) { +fn map_loop(continuation: fn() -> Action(a), f: fn(a) -> b) -> fn() -> Action(b) { fn() { - case continuation1() { + case continuation() { Stop -> Stop - Continue(a, next_a) -> - case continuation2() { - Stop -> Stop - Continue(b, next_b) -> - Continue(fun(a, b), do_map2(next_a, next_b, fun)) - } + Continue(e, continuation) -> Continue(f(e), map_loop(continuation, f)) } } } @@ -428,14 +410,25 @@ pub fn map2( iterator2: Iterator(b), with fun: fn(a, b) -> c, ) -> Iterator(c) { - do_map2(iterator1.continuation, iterator2.continuation, fun) + map2_loop(iterator1.continuation, iterator2.continuation, fun) |> Iterator } -fn do_append(first: fn() -> Action(a), second: fn() -> Action(a)) -> Action(a) { - case first() { - Continue(e, first) -> Continue(e, fn() { do_append(first, second) }) - Stop -> second() +fn map2_loop( + continuation1: fn() -> Action(a), + continuation2: fn() -> Action(b), + with fun: fn(a, b) -> c, +) -> fn() -> Action(c) { + fn() { + case continuation1() { + Stop -> Stop + Continue(a, next_a) -> + case continuation2() { + Stop -> Stop + Continue(b, next_b) -> + Continue(fun(a, b), map2_loop(next_a, next_b, fun)) + } + } } } @@ -454,15 +447,14 @@ fn do_append(first: fn() -> Action(a), second: fn() -> Action(a)) -> Action(a) { /// ``` /// pub fn append(to first: Iterator(a), suffix second: Iterator(a)) -> Iterator(a) { - fn() { do_append(first.continuation, second.continuation) } + fn() { append_loop(first.continuation, second.continuation) } |> Iterator } -fn do_flatten(flattened: fn() -> Action(Iterator(a))) -> Action(a) { - case flattened() { - Stop -> Stop - Continue(it, next_iterator) -> - do_append(it.continuation, fn() { do_flatten(next_iterator) }) +fn append_loop(first: fn() -> Action(a), second: fn() -> Action(a)) -> Action(a) { + case first() { + Continue(e, first) -> Continue(e, fn() { append_loop(first, second) }) + Stop -> second() } } @@ -482,10 +474,18 @@ fn do_flatten(flattened: fn() -> Action(Iterator(a))) -> Action(a) { /// ``` /// pub fn flatten(iterator: Iterator(Iterator(a))) -> Iterator(a) { - fn() { do_flatten(iterator.continuation) } + fn() { flatten_loop(iterator.continuation) } |> Iterator } +fn flatten_loop(flattened: fn() -> Action(Iterator(a))) -> Action(a) { + case flattened() { + Stop -> Stop + Continue(it, next_iterator) -> + append_loop(it.continuation, fn() { flatten_loop(next_iterator) }) + } +} + /// Joins a list of iterators into a single iterator. /// /// This function does not evaluate the elements of the iterator, the @@ -532,20 +532,6 @@ pub fn flat_map( |> flatten } -fn do_filter( - continuation: fn() -> Action(e), - predicate: fn(e) -> Bool, -) -> Action(e) { - case continuation() { - Stop -> Stop - Continue(e, iterator) -> - case predicate(e) { - True -> Continue(e, fn() { do_filter(iterator, predicate) }) - False -> do_filter(iterator, predicate) - } - } -} - /// Creates an iterator from an existing iterator and a predicate function. /// /// The new iterator will contain elements from the first iterator for which @@ -569,20 +555,20 @@ pub fn filter( iterator: Iterator(a), keeping predicate: fn(a) -> Bool, ) -> Iterator(a) { - fn() { do_filter(iterator.continuation, predicate) } + fn() { filter_loop(iterator.continuation, predicate) } |> Iterator } -fn do_filter_map( - continuation: fn() -> Action(a), - f: fn(a) -> Result(b, c), -) -> Action(b) { +fn filter_loop( + continuation: fn() -> Action(e), + predicate: fn(e) -> Bool, +) -> Action(e) { case continuation() { Stop -> Stop - Continue(e, next) -> - case f(e) { - Ok(e) -> Continue(e, fn() { do_filter_map(next, f) }) - Error(_) -> do_filter_map(next, f) + Continue(e, iterator) -> + case predicate(e) { + True -> Continue(e, fn() { filter_loop(iterator, predicate) }) + False -> filter_loop(iterator, predicate) } } } @@ -613,10 +599,24 @@ pub fn filter_map( iterator: Iterator(a), keeping_with f: fn(a) -> Result(b, c), ) -> Iterator(b) { - fn() { do_filter_map(iterator.continuation, f) } + fn() { filter_map_loop(iterator.continuation, f) } |> Iterator } +fn filter_map_loop( + continuation: fn() -> Action(a), + f: fn(a) -> Result(b, c), +) -> Action(b) { + case continuation() { + Stop -> Stop + Continue(e, next) -> + case f(e) { + Ok(e) -> Continue(e, fn() { filter_map_loop(next, f) }) + Error(_) -> filter_map_loop(next, f) + } + } +} + /// Creates an iterator that repeats a given iterator infinitely. /// /// ## Examples @@ -675,17 +675,6 @@ pub fn range(from start: Int, to stop: Int) -> Iterator(Int) { } } -fn do_find(continuation: fn() -> Action(a), f: fn(a) -> Bool) -> Result(a, Nil) { - case continuation() { - Stop -> Error(Nil) - Continue(e, next) -> - case f(e) { - True -> Ok(e) - False -> do_find(next, f) - } - } -} - /// Finds the first element in a given iterator for which the given function returns /// `True`. /// @@ -714,19 +703,19 @@ pub fn find( one_that is_desired: fn(a) -> Bool, ) -> Result(a, Nil) { haystack.continuation - |> do_find(is_desired) + |> find_loop(is_desired) } -fn do_find_map( +fn find_loop( continuation: fn() -> Action(a), - f: fn(a) -> Result(b, c), -) -> Result(b, Nil) { + f: fn(a) -> Bool, +) -> Result(a, Nil) { case continuation() { Stop -> Error(Nil) Continue(e, next) -> case f(e) { - Ok(e) -> Ok(e) - Error(_) -> do_find_map(next, f) + True -> Ok(e) + False -> find_loop(next, f) } } } @@ -759,19 +748,20 @@ pub fn find_map( one_that is_desired: fn(a) -> Result(b, c), ) -> Result(b, Nil) { haystack.continuation - |> do_find_map(is_desired) + |> find_map_loop(is_desired) } -fn do_index( - continuation: fn() -> Action(element), - next: Int, -) -> fn() -> Action(#(element, Int)) { - fn() { - case continuation() { - Stop -> Stop - Continue(e, continuation) -> - Continue(#(e, next), do_index(continuation, next + 1)) - } +fn find_map_loop( + continuation: fn() -> Action(a), + f: fn(a) -> Result(b, c), +) -> Result(b, Nil) { + case continuation() { + Stop -> Error(Nil) + Continue(e, next) -> + case f(e) { + Ok(e) -> Ok(e) + Error(_) -> find_map_loop(next, f) + } } } @@ -786,10 +776,23 @@ fn do_index( /// pub fn index(over iterator: Iterator(element)) -> Iterator(#(element, Int)) { iterator.continuation - |> do_index(0) + |> index_loop(0) |> Iterator } +fn index_loop( + continuation: fn() -> Action(element), + next: Int, +) -> fn() -> Action(#(element, Int)) { + fn() { + case continuation() { + Stop -> Stop + Continue(e, continuation) -> + Continue(#(e, next), index_loop(continuation, next + 1)) + } + } +} + /// Creates an iterator that infinitely applies a function to a value. /// /// ## Examples @@ -806,22 +809,6 @@ pub fn iterate( unfold(initial, fn(element) { Next(element, f(element)) }) } -fn do_take_while( - continuation: fn() -> Action(element), - predicate: fn(element) -> Bool, -) -> fn() -> Action(element) { - fn() { - case continuation() { - Stop -> Stop - Continue(e, next) -> - case predicate(e) { - False -> Stop - True -> Continue(e, do_take_while(next, predicate)) - } - } - } -} - /// Creates an iterator that yields elements while the predicate returns `True`. /// /// ## Examples @@ -838,21 +825,23 @@ pub fn take_while( satisfying predicate: fn(element) -> Bool, ) -> Iterator(element) { iterator.continuation - |> do_take_while(predicate) + |> take_while_loop(predicate) |> Iterator } -fn do_drop_while( +fn take_while_loop( continuation: fn() -> Action(element), predicate: fn(element) -> Bool, -) -> Action(element) { - case continuation() { - Stop -> Stop - Continue(e, next) -> - case predicate(e) { - False -> Continue(e, next) - True -> do_drop_while(next, predicate) - } +) -> fn() -> Action(element) { + fn() { + case continuation() { + Stop -> Stop + Continue(e, next) -> + case predicate(e) { + False -> Stop + True -> Continue(e, take_while_loop(next, predicate)) + } + } } } @@ -872,23 +861,21 @@ pub fn drop_while( in iterator: Iterator(element), satisfying predicate: fn(element) -> Bool, ) -> Iterator(element) { - fn() { do_drop_while(iterator.continuation, predicate) } + fn() { drop_while_loop(iterator.continuation, predicate) } |> Iterator } -fn do_scan( +fn drop_while_loop( continuation: fn() -> Action(element), - f: fn(acc, element) -> acc, - accumulator: acc, -) -> fn() -> Action(acc) { - fn() { - case continuation() { - Stop -> Stop - Continue(el, next) -> { - let accumulated = f(accumulator, el) - Continue(accumulated, do_scan(next, f, accumulated)) + predicate: fn(element) -> Bool, +) -> Action(element) { + case continuation() { + Stop -> Stop + Continue(e, next) -> + case predicate(e) { + False -> Continue(e, next) + True -> drop_while_loop(next, predicate) } - } } } @@ -912,23 +899,22 @@ pub fn scan( with f: fn(acc, element) -> acc, ) -> Iterator(acc) { iterator.continuation - |> do_scan(f, initial) + |> scan_loop(f, initial) |> Iterator } -fn do_zip( - left: fn() -> Action(a), - right: fn() -> Action(b), -) -> fn() -> Action(#(a, b)) { +fn scan_loop( + continuation: fn() -> Action(element), + f: fn(acc, element) -> acc, + accumulator: acc, +) -> fn() -> Action(acc) { fn() { - case left() { + case continuation() { Stop -> Stop - Continue(el_left, next_left) -> - case right() { - Stop -> Stop - Continue(el_right, next_right) -> - Continue(#(el_left, el_right), do_zip(next_left, next_right)) - } + Continue(el, next) -> { + let accumulated = f(accumulator, el) + Continue(accumulated, scan_loop(next, f, accumulated)) + } } } } @@ -946,45 +932,31 @@ fn do_zip( /// ``` /// pub fn zip(left: Iterator(a), right: Iterator(b)) -> Iterator(#(a, b)) { - do_zip(left.continuation, right.continuation) + zip_loop(left.continuation, right.continuation) |> Iterator } -// Result of collecting a single chunk by key -type Chunk(element, key) { - AnotherBy(List(element), key, element, fn() -> Action(element)) - LastBy(List(element)) -} - -fn next_chunk( - continuation: fn() -> Action(element), - f: fn(element) -> key, - previous_key: key, - current_chunk: List(element), -) -> Chunk(element, key) { - case continuation() { - Stop -> LastBy(list.reverse(current_chunk)) - Continue(e, next) -> { - let key = f(e) - case key == previous_key { - True -> next_chunk(next, f, key, [e, ..current_chunk]) - False -> AnotherBy(list.reverse(current_chunk), key, e, next) - } +fn zip_loop( + left: fn() -> Action(a), + right: fn() -> Action(b), +) -> fn() -> Action(#(a, b)) { + fn() { + case left() { + Stop -> Stop + Continue(el_left, next_left) -> + case right() { + Stop -> Stop + Continue(el_right, next_right) -> + Continue(#(el_left, el_right), zip_loop(next_left, next_right)) + } } } } -fn do_chunk( - continuation: fn() -> Action(element), - f: fn(element) -> key, - previous_key: key, - previous_element: element, -) -> Action(List(element)) { - case next_chunk(continuation, f, previous_key, [previous_element]) { - LastBy(chunk) -> Continue(chunk, stop) - AnotherBy(chunk, key, el, next) -> - Continue(chunk, fn() { do_chunk(next, f, key, el) }) - } +// Result of collecting a single chunk by key +type Chunk(element, key) { + AnotherBy(List(element), key, element, fn() -> Action(element)) + LastBy(List(element)) } /// Creates an iterator that emits chunks of elements @@ -1006,54 +978,43 @@ pub fn chunk( fn() { case iterator.continuation() { Stop -> Stop - Continue(e, next) -> do_chunk(next, f, f(e), e) + Continue(e, next) -> chunk_loop(next, f, f(e), e) } } |> Iterator } -// Result of collecting a single sized chunk -type SizedChunk(element) { - Another(List(element), fn() -> Action(element)) - Last(List(element)) - NoMore +fn chunk_loop( + continuation: fn() -> Action(element), + f: fn(element) -> key, + previous_key: key, + previous_element: element, +) -> Action(List(element)) { + case next_chunk(continuation, f, previous_key, [previous_element]) { + LastBy(chunk) -> Continue(chunk, stop) + AnotherBy(chunk, key, el, next) -> + Continue(chunk, fn() { chunk_loop(next, f, key, el) }) + } } -fn next_sized_chunk( +fn next_chunk( continuation: fn() -> Action(element), - left: Int, + f: fn(element) -> key, + previous_key: key, current_chunk: List(element), -) -> SizedChunk(element) { +) -> Chunk(element, key) { case continuation() { - Stop -> - case current_chunk { - [] -> NoMore - remaining -> Last(list.reverse(remaining)) - } + Stop -> LastBy(list.reverse(current_chunk)) Continue(e, next) -> { - let chunk = [e, ..current_chunk] - case left > 1 { - False -> Another(list.reverse(chunk), next) - True -> next_sized_chunk(next, left - 1, chunk) + let key = f(e) + case key == previous_key { + True -> next_chunk(next, f, key, [e, ..current_chunk]) + False -> AnotherBy(list.reverse(current_chunk), key, e, next) } } } } -fn do_sized_chunk( - continuation: fn() -> Action(element), - count: Int, -) -> fn() -> Action(List(element)) { - fn() { - case next_sized_chunk(continuation, count, []) { - NoMore -> Stop - Last(chunk) -> Continue(chunk, stop) - Another(chunk, next_element) -> - Continue(chunk, do_sized_chunk(next_element, count)) - } - } -} - /// Creates an iterator that emits chunks of given size. /// /// If the last chunk does not have `count` elements, it is yielded @@ -1082,19 +1043,48 @@ pub fn sized_chunk( into count: Int, ) -> Iterator(List(element)) { iterator.continuation - |> do_sized_chunk(count) + |> sized_chunk_loop(count) |> Iterator } -fn do_intersperse( +fn sized_chunk_loop( continuation: fn() -> Action(element), - separator: element, -) -> Action(element) { + count: Int, +) -> fn() -> Action(List(element)) { + fn() { + case next_sized_chunk(continuation, count, []) { + NoMore -> Stop + Last(chunk) -> Continue(chunk, stop) + Another(chunk, next_element) -> + Continue(chunk, sized_chunk_loop(next_element, count)) + } + } +} + +// Result of collecting a single sized chunk +type SizedChunk(element) { + Another(List(element), fn() -> Action(element)) + Last(List(element)) + NoMore +} + +fn next_sized_chunk( + continuation: fn() -> Action(element), + left: Int, + current_chunk: List(element), +) -> SizedChunk(element) { case continuation() { - Stop -> Stop + Stop -> + case current_chunk { + [] -> NoMore + remaining -> Last(list.reverse(remaining)) + } Continue(e, next) -> { - let next_interspersed = fn() { do_intersperse(next, separator) } - Continue(separator, fn() { Continue(e, next_interspersed) }) + let chunk = [e, ..current_chunk] + case left > 1 { + False -> Another(list.reverse(chunk), next) + True -> next_sized_chunk(next, left - 1, chunk) + } } } } @@ -1132,23 +1122,22 @@ pub fn intersperse( fn() { case iterator.continuation() { Stop -> Stop - Continue(e, next) -> Continue(e, fn() { do_intersperse(next, elem) }) + Continue(e, next) -> Continue(e, fn() { intersperse_loop(next, elem) }) } } |> Iterator } -fn do_any( +fn intersperse_loop( continuation: fn() -> Action(element), - predicate: fn(element) -> Bool, -) -> Bool { + separator: element, +) -> Action(element) { case continuation() { - Stop -> False - Continue(e, next) -> - case predicate(e) { - True -> True - False -> do_any(next, predicate) - } + Stop -> Stop + Continue(e, next) -> { + let next_interspersed = fn() { intersperse_loop(next, separator) } + Continue(separator, fn() { Continue(e, next_interspersed) }) + } } } @@ -1184,19 +1173,19 @@ pub fn any( satisfying predicate: fn(element) -> Bool, ) -> Bool { iterator.continuation - |> do_any(predicate) + |> any_loop(predicate) } -fn do_all( +fn any_loop( continuation: fn() -> Action(element), predicate: fn(element) -> Bool, ) -> Bool { case continuation() { - Stop -> True + Stop -> False Continue(e, next) -> case predicate(e) { - True -> do_all(next, predicate) - False -> False + True -> True + False -> any_loop(next, predicate) } } } @@ -1233,24 +1222,20 @@ pub fn all( satisfying predicate: fn(element) -> Bool, ) -> Bool { iterator.continuation - |> do_all(predicate) -} - -fn update_group_with(el: element) -> fn(Option(List(element))) -> List(element) { - fn(maybe_group) { - case maybe_group { - Some(group) -> [el, ..group] - None -> [el] - } - } + |> all_loop(predicate) } -fn group_updater( - f: fn(element) -> key, -) -> fn(Dict(key, List(element)), element) -> Dict(key, List(element)) { - fn(groups, elem) { - groups - |> dict.upsert(f(elem), update_group_with(elem)) +fn all_loop( + continuation: fn() -> Action(element), + predicate: fn(element) -> Bool, +) -> Bool { + case continuation() { + Stop -> True + Continue(e, next) -> + case predicate(e) { + True -> all_loop(next, predicate) + False -> False + } } } @@ -1276,6 +1261,24 @@ pub fn group( |> dict.map_values(fn(_, group) { list.reverse(group) }) } +fn group_updater( + f: fn(element) -> key, +) -> fn(Dict(key, List(element)), element) -> Dict(key, List(element)) { + fn(groups, elem) { + groups + |> dict.upsert(f(elem), update_group_with(elem)) + } +} + +fn update_group_with(el: element) -> fn(Option(List(element))) -> List(element) { + fn(maybe_group) { + case maybe_group { + Some(group) -> [el, ..group] + None -> [el] + } + } +} + /// This function acts similar to fold, but does not take an initial state. /// Instead, it starts from the first yielded element /// and combines it with each subsequent element in turn using the given function. @@ -1304,7 +1307,7 @@ pub fn reduce( case iterator.continuation() { Stop -> Error(Nil) Continue(e, next) -> - do_fold(next, f, e) + fold_loop(next, f, e) |> Ok } } @@ -1372,17 +1375,6 @@ pub fn single(elem: element) -> Iterator(element) { once(fn() { elem }) } -fn do_interleave( - current: fn() -> Action(element), - next: fn() -> Action(element), -) -> Action(element) { - case current() { - Stop -> next() - Continue(e, next_other) -> - Continue(e, fn() { do_interleave(next, next_other) }) - } -} - /// Creates an iterator that alternates between the two given iterators /// until both have run out. /// @@ -1406,22 +1398,18 @@ pub fn interleave( left: Iterator(element), with right: Iterator(element), ) -> Iterator(element) { - fn() { do_interleave(left.continuation, right.continuation) } + fn() { interleave_loop(left.continuation, right.continuation) } |> Iterator } -fn do_fold_until( - continuation: fn() -> Action(e), - f: fn(acc, e) -> list.ContinueOrStop(acc), - accumulator: acc, -) -> acc { - case continuation() { - Stop -> accumulator - Continue(elem, next) -> - case f(accumulator, elem) { - list.Continue(accumulator) -> do_fold_until(next, f, accumulator) - list.Stop(accumulator) -> accumulator - } +fn interleave_loop( + current: fn() -> Action(element), + next: fn() -> Action(element), +) -> Action(element) { + case current() { + Stop -> next() + Continue(e, next_other) -> + Continue(e, fn() { interleave_loop(next, next_other) }) } } @@ -1455,22 +1443,21 @@ pub fn fold_until( with f: fn(acc, e) -> list.ContinueOrStop(acc), ) -> acc { iterator.continuation - |> do_fold_until(f, initial) + |> fold_until_loop(f, initial) } -fn do_try_fold( - over continuation: fn() -> Action(a), - with f: fn(acc, a) -> Result(acc, err), - from accumulator: acc, -) -> Result(acc, err) { +fn fold_until_loop( + continuation: fn() -> Action(e), + f: fn(acc, e) -> list.ContinueOrStop(acc), + accumulator: acc, +) -> acc { case continuation() { - Stop -> Ok(accumulator) - Continue(elem, next) -> { + Stop -> accumulator + Continue(elem, next) -> case f(accumulator, elem) { - Ok(result) -> do_try_fold(next, f, result) - Error(_) as error -> error + list.Continue(accumulator) -> fold_until_loop(next, f, accumulator) + list.Stop(accumulator) -> accumulator } - } } } @@ -1499,7 +1486,23 @@ pub fn try_fold( with f: fn(acc, e) -> Result(acc, err), ) -> Result(acc, err) { iterator.continuation - |> do_try_fold(f, initial) + |> try_fold_loop(f, initial) +} + +fn try_fold_loop( + over continuation: fn() -> Action(a), + with f: fn(acc, a) -> Result(acc, err), + from accumulator: acc, +) -> Result(acc, err) { + case continuation() { + Stop -> Ok(accumulator) + Continue(elem, next) -> { + case f(accumulator, elem) { + Ok(result) -> try_fold_loop(next, f, result) + Error(_) as error -> error + } + } + } } /// Returns the first element yielded by the given iterator, if it exists, @@ -1552,13 +1555,6 @@ pub fn at(in iterator: Iterator(e), get index: Int) -> Result(e, Nil) { |> first } -fn do_length(over continuation: fn() -> Action(e), with length: Int) -> Int { - case continuation() { - Stop -> length - Continue(_, next) -> do_length(next, length + 1) - } -} - /// Counts the number of elements in the given iterator. /// /// This function has to traverse the entire iterator to count its elements, @@ -1578,7 +1574,14 @@ fn do_length(over continuation: fn() -> Action(e), with length: Int) -> Int { /// pub fn length(over iterator: Iterator(e)) -> Int { iterator.continuation - |> do_length(0) + |> length_loop(0) +} + +fn length_loop(over continuation: fn() -> Action(e), with length: Int) -> Int { + case continuation() { + Stop -> length + Continue(_, next) -> length_loop(next, length + 1) + } } /// Traverse an iterator, calling a function on each element. diff --git a/src/gleam/list.gleam b/src/gleam/list.gleam index c363cda8..97084db3 100644 --- a/src/gleam/list.gleam +++ b/src/gleam/list.gleam @@ -124,13 +124,13 @@ pub fn count(list: List(a), where predicate: fn(a) -> Bool) -> Int { /// @external(erlang, "lists", "reverse") pub fn reverse(list: List(a)) -> List(a) { - do_reverse(list, []) + reverse_loop(list, []) } -fn do_reverse(remaining: List(a), accumulator: List(a)) -> List(a) { +fn reverse_loop(remaining: List(a), accumulator: List(a)) -> List(a) { case remaining { [] -> accumulator - [item, ..rest] -> do_reverse(rest, [item, ..accumulator]) + [item, ..rest] -> reverse_loop(rest, [item, ..accumulator]) } } @@ -299,19 +299,6 @@ pub fn group(list: List(v), by key: fn(v) -> k) -> Dict(k, List(v)) { fold(list, dict.new(), update_group(key)) } -fn do_filter(list: List(a), fun: fn(a) -> Bool, acc: List(a)) -> List(a) { - case list { - [] -> reverse(acc) - [first, ..rest] -> { - let new_acc = case fun(first) { - True -> [first, ..acc] - False -> acc - } - do_filter(rest, fun, new_acc) - } - } -} - /// Returns a new list containing only the elements from the first list for /// which the given functions returns `True`. /// @@ -328,22 +315,18 @@ fn do_filter(list: List(a), fun: fn(a) -> Bool, acc: List(a)) -> List(a) { /// ``` /// pub fn filter(list: List(a), keeping predicate: fn(a) -> Bool) -> List(a) { - do_filter(list, predicate, []) + filter_loop(list, predicate, []) } -fn do_filter_map( - list: List(a), - fun: fn(a) -> Result(b, e), - acc: List(b), -) -> List(b) { +fn filter_loop(list: List(a), fun: fn(a) -> Bool, acc: List(a)) -> List(a) { case list { [] -> reverse(acc) [first, ..rest] -> { let new_acc = case fun(first) { - Ok(first) -> [first, ..acc] - Error(_) -> acc + True -> [first, ..acc] + False -> acc } - do_filter_map(rest, fun, new_acc) + filter_loop(rest, fun, new_acc) } } } @@ -364,13 +347,23 @@ fn do_filter_map( /// ``` /// pub fn filter_map(list: List(a), with fun: fn(a) -> Result(b, e)) -> List(b) { - do_filter_map(list, fun, []) + filter_map_loop(list, fun, []) } -fn do_map(list: List(a), fun: fn(a) -> b, acc: List(b)) -> List(b) { +fn filter_map_loop( + list: List(a), + fun: fn(a) -> Result(b, e), + acc: List(b), +) -> List(b) { case list { [] -> reverse(acc) - [first, ..rest] -> do_map(rest, fun, [fun(first), ..acc]) + [first, ..rest] -> { + let new_acc = case fun(first) { + Ok(first) -> [first, ..acc] + Error(_) -> acc + } + filter_map_loop(rest, fun, new_acc) + } } } @@ -385,7 +378,14 @@ fn do_map(list: List(a), fun: fn(a) -> b, acc: List(b)) -> List(b) { /// ``` /// pub fn map(list: List(a), with fun: fn(a) -> b) -> List(b) { - do_map(list, fun, []) + map_loop(list, fun, []) +} + +fn map_loop(list: List(a), fun: fn(a) -> b, acc: List(b)) -> List(b) { + case list { + [] -> reverse(acc) + [first, ..rest] -> map_loop(rest, fun, [fun(first), ..acc]) + } } /// Combines two lists into a single list using the given function. @@ -405,10 +405,10 @@ pub fn map(list: List(a), with fun: fn(a) -> b) -> List(b) { /// ``` /// pub fn map2(list1: List(a), list2: List(b), with fun: fn(a, b) -> c) -> List(c) { - do_map2(list1, list2, fun, []) + map2_loop(list1, list2, fun, []) } -fn do_map2( +fn map2_loop( list1: List(a), list2: List(b), fun: fn(a, b) -> c, @@ -416,7 +416,7 @@ fn do_map2( ) -> List(c) { case list1, list2 { [], _ | _, [] -> reverse(acc) - [a, ..as_], [b, ..bs] -> do_map2(as_, bs, fun, [fun(a, b), ..acc]) + [a, ..as_], [b, ..bs] -> map2_loop(as_, bs, fun, [fun(a, b), ..acc]) } } @@ -446,21 +446,6 @@ pub fn map_fold( |> pair.map_second(reverse) } -fn do_index_map( - list: List(a), - fun: fn(a, Int) -> b, - index: Int, - acc: List(b), -) -> List(b) { - case list { - [] -> reverse(acc) - [first, ..rest] -> { - let acc = [fun(first, index), ..acc] - do_index_map(rest, fun, index + 1, acc) - } - } -} - /// Returns a new list containing only the elements of the first list after the /// function has been applied to each one and their index. /// @@ -475,21 +460,21 @@ fn do_index_map( /// ``` /// pub fn index_map(list: List(a), with fun: fn(a, Int) -> b) -> List(b) { - do_index_map(list, fun, 0, []) + index_map_loop(list, fun, 0, []) } -fn do_try_map( +fn index_map_loop( list: List(a), - fun: fn(a) -> Result(b, e), + fun: fn(a, Int) -> b, + index: Int, acc: List(b), -) -> Result(List(b), e) { +) -> List(b) { case list { - [] -> Ok(reverse(acc)) - [first, ..rest] -> - case fun(first) { - Ok(first) -> do_try_map(rest, fun, [first, ..acc]) - Error(error) -> Error(error) - } + [] -> reverse(acc) + [first, ..rest] -> { + let acc = [fun(first, index), ..acc] + index_map_loop(rest, fun, index + 1, acc) + } } } @@ -529,7 +514,22 @@ pub fn try_map( over list: List(a), with fun: fn(a) -> Result(b, e), ) -> Result(List(b), e) { - do_try_map(list, fun, []) + try_map_loop(list, fun, []) +} + +fn try_map_loop( + list: List(a), + fun: fn(a) -> Result(b, e), + acc: List(b), +) -> Result(List(b), e) { + case list { + [] -> Ok(reverse(acc)) + [first, ..rest] -> + case fun(first) { + Ok(first) -> try_map_loop(rest, fun, [first, ..acc]) + Error(error) -> Error(error) + } + } } /// Returns a list that is the given list with up to the given number of @@ -563,17 +563,6 @@ pub fn drop(from list: List(a), up_to n: Int) -> List(a) { } } -fn do_take(list: List(a), n: Int, acc: List(a)) -> List(a) { - case n <= 0 { - True -> reverse(acc) - False -> - case list { - [] -> reverse(acc) - [first, ..rest] -> do_take(rest, n - 1, [first, ..acc]) - } - } -} - /// Returns a list containing the first given number of elements from the given /// list. /// @@ -595,7 +584,18 @@ fn do_take(list: List(a), n: Int, acc: List(a)) -> List(a) { /// ``` /// pub fn take(from list: List(a), up_to n: Int) -> List(a) { - do_take(list, n, []) + take_loop(list, n, []) +} + +fn take_loop(list: List(a), n: Int, acc: List(a)) -> List(a) { + case n <= 0 { + True -> reverse(acc) + False -> + case list { + [] -> reverse(acc) + [first, ..rest] -> take_loop(rest, n - 1, [first, ..acc]) + } + } } /// Returns a new empty list. @@ -645,13 +645,13 @@ pub fn wrap(item: a) -> List(a) { /// @external(erlang, "lists", "append") pub fn append(first: List(a), second: List(a)) -> List(a) { - do_append(reverse(first), second) + append_loop(reverse(first), second) } -fn do_append(first: List(a), second: List(a)) -> List(a) { +fn append_loop(first: List(a), second: List(a)) -> List(a) { case first { [] -> second - [item, ..rest] -> do_append(rest, [item, ..second]) + [item, ..rest] -> append_loop(rest, [item, ..second]) } } @@ -680,14 +680,6 @@ fn reverse_and_prepend(list prefix: List(a), to suffix: List(a)) -> List(a) { } } -fn do_concat(lists: List(List(a)), acc: List(a)) -> List(a) { - case lists { - [] -> reverse(acc) - [list, ..further_lists] -> - do_concat(further_lists, reverse_and_prepend(list: list, to: acc)) - } -} - /// Joins a list of lists into a single list. /// /// This function traverses all elements twice. @@ -701,7 +693,15 @@ fn do_concat(lists: List(List(a)), acc: List(a)) -> List(a) { /// @deprecated("Use `list.flatten` instead.") pub fn concat(lists: List(List(a))) -> List(a) { - do_concat(lists, []) + concat_loop(lists, []) +} + +fn concat_loop(lists: List(List(a)), acc: List(a)) -> List(a) { + case lists { + [] -> reverse(acc) + [list, ..further_lists] -> + concat_loop(further_lists, reverse_and_prepend(list: list, to: acc)) + } } /// This is the same as `concat`: it joins a list of lists into a single @@ -717,7 +717,7 @@ pub fn concat(lists: List(List(a))) -> List(a) { /// ``` /// pub fn flatten(lists: List(List(a))) -> List(a) { - do_concat(lists, []) + concat_loop(lists, []) } /// Maps the list with the given function into a list of lists, and then flattens it. @@ -775,19 +775,6 @@ pub fn fold_right( } } -fn do_index_fold( - over: List(a), - acc: acc, - with: fn(acc, a, Int) -> acc, - index: Int, -) -> acc { - case over { - [] -> acc - [first, ..rest] -> - do_index_fold(rest, with(acc, first, index), with, index + 1) - } -} - /// Like fold but the folding function also receives the index of the current element. /// /// ## Examples @@ -802,7 +789,20 @@ pub fn index_fold( from initial: acc, with fun: fn(acc, a, Int) -> acc, ) -> acc { - do_index_fold(list, initial, fun, 0) + index_fold_loop(list, initial, fun, 0) +} + +fn index_fold_loop( + over: List(a), + acc: acc, + with: fn(acc, a, Int) -> acc, + index: Int, +) -> acc { + case over { + [] -> acc + [first, ..rest] -> + index_fold_loop(rest, with(acc, first, index), with, index + 1) + } } /// A variant of fold that might fail. @@ -1019,14 +1019,6 @@ pub fn any(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool { } } -fn do_zip(one: List(a), other: List(b), acc: List(#(a, b))) -> List(#(a, b)) { - case one, other { - [first_one, ..rest_one], [first_other, ..rest_other] -> - do_zip(rest_one, rest_other, [#(first_one, first_other), ..acc]) - _, _ -> reverse(acc) - } -} - /// Takes two lists and returns a single list of 2-element tuples. /// /// If one of the lists is longer than the other, the remaining elements from @@ -1055,7 +1047,15 @@ fn do_zip(one: List(a), other: List(b), acc: List(#(a, b))) -> List(#(a, b)) { /// ``` /// pub fn zip(list: List(a), with other: List(b)) -> List(#(a, b)) { - do_zip(list, other, []) + zip_loop(list, other, []) +} + +fn zip_loop(one: List(a), other: List(b), acc: List(#(a, b))) -> List(#(a, b)) { + case one, other { + [first_one, ..rest_one], [first_other, ..rest_other] -> + zip_loop(rest_one, rest_other, [#(first_one, first_other), ..acc]) + _, _ -> reverse(acc) + } } /// Takes two lists and returns a single list of 2-element tuples. @@ -1094,18 +1094,6 @@ pub fn strict_zip( } } -fn do_unzip( - input: List(#(a, b)), - one: List(a), - other: List(b), -) -> #(List(a), List(b)) { - case input { - [] -> #(reverse(one), reverse(other)) - [#(first_one, first_other), ..rest] -> - do_unzip(rest, [first_one, ..one], [first_other, ..other]) - } -} - /// Takes a single list of 2-element tuples and returns two lists. /// /// ## Examples @@ -1121,13 +1109,18 @@ fn do_unzip( /// ``` /// pub fn unzip(input: List(#(a, b))) -> #(List(a), List(b)) { - do_unzip(input, [], []) + unzip_loop(input, [], []) } -fn do_intersperse(list: List(a), separator: a, acc: List(a)) -> List(a) { - case list { - [] -> reverse(acc) - [x, ..rest] -> do_intersperse(rest, separator, [x, separator, ..acc]) +fn unzip_loop( + input: List(#(a, b)), + one: List(a), + other: List(b), +) -> #(List(a), List(b)) { + case input { + [] -> #(reverse(one), reverse(other)) + [#(first_one, first_other), ..rest] -> + unzip_loop(rest, [first_one, ..one], [first_other, ..other]) } } @@ -1150,7 +1143,14 @@ fn do_intersperse(list: List(a), separator: a, acc: List(a)) -> List(a) { pub fn intersperse(list: List(a), with elem: a) -> List(a) { case list { [] | [_] -> list - [x, ..rest] -> do_intersperse(rest, elem, [x]) + [x, ..rest] -> intersperse_loop(rest, elem, [x]) + } +} + +fn intersperse_loop(list: List(a), separator: a, acc: List(a)) -> List(a) { + case list { + [] -> reverse(acc) + [x, ..rest] -> intersperse_loop(rest, separator, [x, separator, ..acc]) } } @@ -1264,7 +1264,7 @@ fn sequences( // Notice how we have to reverse the accumulator we're growing: since // we always add items to the head, `growing` is built in the opposite // sorting order of what it actually is in the original list. - Ascending -> [do_reverse(growing, []), ..acc] + Ascending -> [reverse_loop(growing, []), ..acc] Descending -> [growing, ..acc] } @@ -1285,7 +1285,7 @@ fn sequences( // be the one we just found. order.Gt, Ascending | order.Lt, Descending | order.Eq, Descending -> { let acc = case direction { - Ascending -> [do_reverse(growing, []), ..acc] + Ascending -> [reverse_loop(growing, []), ..acc] Descending -> [growing, ..acc] } case rest { @@ -1326,7 +1326,7 @@ fn merge_all( // If we have a single list in descending order, we reverse it to make sure // it's in ascending order and we're done. - [sequence], Descending -> do_reverse(sequence, []) + [sequence], Descending -> reverse_loop(sequence, []) // Merging together sequences that are in ascending (descending) order // reverses their order, so the recursive call will assume to be merging @@ -1353,12 +1353,12 @@ fn merge_ascending_pairs( acc: List(List(a)), ) { case sequences { - [] -> do_reverse(acc, []) + [] -> reverse_loop(acc, []) // Beware, if we have just one item left we must reverse it: we take // ascending lists as input and have to return descending ones. // If we returned it like it is it would be sorted in ascending order. - [sequence] -> do_reverse([do_reverse(sequence, []), ..acc], []) + [sequence] -> reverse_loop([reverse_loop(sequence, []), ..acc], []) [ascending1, ascending2, ..rest] -> { let descending = merge_ascendings(ascending1, ascending2, compare, []) @@ -1375,9 +1375,9 @@ fn merge_descending_pairs( acc: List(List(a)), ) { case sequences { - [] -> do_reverse(acc, []) + [] -> reverse_loop(acc, []) - [sequence] -> do_reverse([do_reverse(sequence, []), ..acc], []) + [sequence] -> reverse_loop([reverse_loop(sequence, []), ..acc], []) [descending1, descending2, ..rest] -> { let ascending = merge_descendings(descending1, descending2, compare, []) @@ -1401,7 +1401,7 @@ fn merge_ascendings( acc: List(a), ) -> List(a) { case list1, list2 { - [], list | list, [] -> do_reverse(list, acc) + [], list | list, [] -> reverse_loop(list, acc) [first1, ..rest1], [first2, ..rest2] -> case compare(first1, first2) { @@ -1428,7 +1428,7 @@ fn merge_descendings( acc: List(a), ) -> List(a) { case list1, list2 { - [], list | list, [] -> do_reverse(list, acc) + [], list | list, [] -> reverse_loop(list, acc) [first1, ..rest1], [first2, ..rest2] -> case compare(first1, first2) { order.Lt -> merge_descendings(list1, rest2, compare, [first2, ..acc]) @@ -1458,21 +1458,14 @@ fn merge_descendings( /// ``` /// pub fn range(from start: Int, to stop: Int) -> List(Int) { - tail_recursive_range(start, stop, []) + range_loop(start, stop, []) } -fn tail_recursive_range(start: Int, stop: Int, acc: List(Int)) -> List(Int) { +fn range_loop(start: Int, stop: Int, acc: List(Int)) -> List(Int) { case int.compare(start, stop) { order.Eq -> [stop, ..acc] - order.Gt -> tail_recursive_range(start, stop + 1, [stop, ..acc]) - order.Lt -> tail_recursive_range(start, stop - 1, [stop, ..acc]) - } -} - -fn do_repeat(item: a, times: Int, acc: List(a)) -> List(a) { - case times <= 0 { - True -> acc - False -> do_repeat(item, times - 1, [item, ..acc]) + order.Gt -> range_loop(start, stop + 1, [stop, ..acc]) + order.Lt -> range_loop(start, stop - 1, [stop, ..acc]) } } @@ -1491,17 +1484,13 @@ fn do_repeat(item: a, times: Int, acc: List(a)) -> List(a) { /// ``` /// pub fn repeat(item a: a, times times: Int) -> List(a) { - do_repeat(a, times, []) + repeat_loop(a, times, []) } -fn do_split(list: List(a), n: Int, taken: List(a)) -> #(List(a), List(a)) { - case n <= 0 { - True -> #(reverse(taken), list) - False -> - case list { - [] -> #(reverse(taken), []) - [first, ..rest] -> do_split(rest, n - 1, [first, ..taken]) - } +fn repeat_loop(item: a, times: Int, acc: List(a)) -> List(a) { + case times <= 0 { + True -> acc + False -> repeat_loop(item, times - 1, [item, ..acc]) } } @@ -1528,20 +1517,16 @@ fn do_split(list: List(a), n: Int, taken: List(a)) -> #(List(a), List(a)) { /// ``` /// pub fn split(list list: List(a), at index: Int) -> #(List(a), List(a)) { - do_split(list, index, []) + split_loop(list, index, []) } -fn do_split_while( - list: List(a), - f: fn(a) -> Bool, - acc: List(a), -) -> #(List(a), List(a)) { - case list { - [] -> #(reverse(acc), []) - [first, ..rest] -> - case f(first) { - False -> #(reverse(acc), list) - _ -> do_split_while(rest, f, [first, ..acc]) +fn split_loop(list: List(a), n: Int, taken: List(a)) -> #(List(a), List(a)) { + case n <= 0 { + True -> #(reverse(taken), list) + False -> + case list { + [] -> #(reverse(taken), []) + [first, ..rest] -> split_loop(rest, n - 1, [first, ..taken]) } } } @@ -1568,7 +1553,22 @@ pub fn split_while( list list: List(a), satisfying predicate: fn(a) -> Bool, ) -> #(List(a), List(a)) { - do_split_while(list, predicate, []) + split_while_loop(list, predicate, []) +} + +fn split_while_loop( + list: List(a), + f: fn(a) -> Bool, + acc: List(a), +) -> #(List(a), List(a)) { + case list { + [] -> #(reverse(acc), []) + [first, ..rest] -> + case f(first) { + False -> #(reverse(acc), list) + _ -> split_while_loop(rest, f, [first, ..acc]) + } + } } /// Given a list of 2-element tuples, finds the first tuple that has a given @@ -1640,17 +1640,6 @@ pub fn key_filter( }) } -fn do_pop(haystack, predicate, checked) { - case haystack { - [] -> Error(Nil) - [x, ..rest] -> - case predicate(x) { - True -> Ok(#(x, append(reverse(checked), rest))) - False -> do_pop(rest, predicate, [x, ..checked]) - } - } -} - /// Removes the first element in a given list for which the predicate function returns `True`. /// /// Returns `Error(Nil)` if no such element is found. @@ -1676,20 +1665,16 @@ pub fn pop( in list: List(a), one_that is_desired: fn(a) -> Bool, ) -> Result(#(a, List(a)), Nil) { - do_pop(list, is_desired, []) + pop_loop(list, is_desired, []) } -fn do_pop_map( - list: List(a), - mapper: fn(a) -> Result(b, e), - checked: List(a), -) -> Result(#(b, List(a)), Nil) { - case list { +fn pop_loop(haystack, predicate, checked) { + case haystack { [] -> Error(Nil) [x, ..rest] -> - case mapper(x) { - Ok(y) -> Ok(#(y, append(reverse(checked), rest))) - Error(_) -> do_pop_map(rest, mapper, [x, ..checked]) + case predicate(x) { + True -> Ok(#(x, append(reverse(checked), rest))) + False -> pop_loop(rest, predicate, [x, ..checked]) } } } @@ -1720,7 +1705,22 @@ pub fn pop_map( in haystack: List(a), one_that is_desired: fn(a) -> Result(b, c), ) -> Result(#(b, List(a)), Nil) { - do_pop_map(haystack, is_desired, []) + pop_map_loop(haystack, is_desired, []) +} + +fn pop_map_loop( + list: List(a), + mapper: fn(a) -> Result(b, e), + checked: List(a), +) -> Result(#(b, List(a)), Nil) { + case list { + [] -> Error(Nil) + [x, ..rest] -> + case mapper(x) { + Ok(y) -> Ok(#(y, append(reverse(checked), rest))) + Error(_) -> pop_map_loop(rest, mapper, [x, ..checked]) + } + } } /// Given a list of 2-element tuples, finds the first tuple that has a given @@ -1835,17 +1835,6 @@ pub fn try_each( } } -fn do_partition(list, categorise, trues, falses) { - case list { - [] -> #(reverse(trues), reverse(falses)) - [first, ..rest] -> - case categorise(first) { - True -> do_partition(rest, categorise, [first, ..trues], falses) - False -> do_partition(rest, categorise, trues, [first, ..falses]) - } - } -} - /// Partitions a list into a tuple/pair of lists /// by a given categorisation function. /// @@ -1862,7 +1851,18 @@ pub fn partition( list: List(a), with categorise: fn(a) -> Bool, ) -> #(List(a), List(a)) { - do_partition(list, categorise, [], []) + partition_loop(list, categorise, [], []) +} + +fn partition_loop(list, categorise, trues, falses) { + case list { + [] -> #(reverse(trues), reverse(falses)) + [first, ..rest] -> + case categorise(first) { + True -> partition_loop(rest, categorise, [first, ..trues], falses) + False -> partition_loop(rest, categorise, trues, [first, ..falses]) + } + } } /// Returns all the permutations of a list. @@ -1893,15 +1893,6 @@ pub fn permutations(list: List(a)) -> List(List(a)) { } } -fn do_window(acc: List(List(a)), list: List(a), n: Int) -> List(List(a)) { - let window = take(list, n) - - case length(window) == n { - True -> do_window([window, ..acc], drop(list, 1), n) - False -> acc - } -} - /// Returns a list of sliding windows. /// /// ## Examples @@ -1919,7 +1910,16 @@ fn do_window(acc: List(List(a)), list: List(a), n: Int) -> List(List(a)) { pub fn window(list: List(a), by n: Int) -> List(List(a)) { case n <= 0 { True -> [] - False -> do_window([], list, n) |> reverse + False -> window_loop([], list, n) + } +} + +fn window_loop(acc: List(List(a)), list: List(a), n: Int) -> List(List(a)) { + let window = take(list, n) + + case length(window) == n { + True -> window_loop([window, ..acc], drop(list, 1), n) + False -> reverse(acc) } } @@ -1964,21 +1964,6 @@ pub fn drop_while( } } -fn do_take_while( - list: List(a), - predicate: fn(a) -> Bool, - acc: List(a), -) -> List(a) { - case list { - [] -> reverse(acc) - [first, ..rest] -> - case predicate(first) { - True -> do_take_while(rest, predicate, [first, ..acc]) - False -> reverse(acc) - } - } -} - /// Takes the first elements in a given list for which the predicate function returns `True`. /// /// ## Examples @@ -1992,28 +1977,21 @@ pub fn take_while( in list: List(a), satisfying predicate: fn(a) -> Bool, ) -> List(a) { - do_take_while(list, predicate, []) + take_while_loop(list, predicate, []) } -fn do_chunk( +fn take_while_loop( list: List(a), - f: fn(a) -> k, - previous_key: k, - current_chunk: List(a), - acc: List(List(a)), -) -> List(List(a)) { + predicate: fn(a) -> Bool, + acc: List(a), +) -> List(a) { case list { - [first, ..rest] -> { - let key = f(first) - case key == previous_key { - False -> { - let new_acc = [reverse(current_chunk), ..acc] - do_chunk(rest, f, key, [first], new_acc) - } - _true -> do_chunk(rest, f, key, [first, ..current_chunk], acc) + [] -> reverse(acc) + [first, ..rest] -> + case predicate(first) { + True -> take_while_loop(rest, predicate, [first, ..acc]) + False -> reverse(acc) } - } - _empty -> reverse([reverse(current_chunk), ..acc]) } } @@ -2030,30 +2008,29 @@ fn do_chunk( pub fn chunk(in list: List(a), by f: fn(a) -> k) -> List(List(a)) { case list { [] -> [] - [first, ..rest] -> do_chunk(rest, f, f(first), [first], []) + [first, ..rest] -> chunk_loop(rest, f, f(first), [first], []) } } -fn do_sized_chunk( +fn chunk_loop( list: List(a), - count: Int, - left: Int, + f: fn(a) -> k, + previous_key: k, current_chunk: List(a), acc: List(List(a)), ) -> List(List(a)) { case list { - [] -> - case current_chunk { - [] -> reverse(acc) - remaining -> reverse([reverse(remaining), ..acc]) - } [first, ..rest] -> { - let chunk = [first, ..current_chunk] - case left > 1 { - False -> do_sized_chunk(rest, count, count, [], [reverse(chunk), ..acc]) - True -> do_sized_chunk(rest, count, left - 1, chunk, acc) + let key = f(first) + case key == previous_key { + False -> { + let new_acc = [reverse(current_chunk), ..acc] + chunk_loop(rest, f, key, [first], new_acc) + } + _true -> chunk_loop(rest, f, key, [first, ..current_chunk], acc) } } + _empty -> reverse([reverse(current_chunk), ..acc]) } } @@ -2077,7 +2054,31 @@ fn do_sized_chunk( /// ``` /// pub fn sized_chunk(in list: List(a), into count: Int) -> List(List(a)) { - do_sized_chunk(list, count, count, [], []) + sized_chunk_loop(list, count, count, [], []) +} + +fn sized_chunk_loop( + list: List(a), + count: Int, + left: Int, + current_chunk: List(a), + acc: List(List(a)), +) -> List(List(a)) { + case list { + [] -> + case current_chunk { + [] -> reverse(acc) + remaining -> reverse([reverse(remaining), ..acc]) + } + [first, ..rest] -> { + let chunk = [first, ..current_chunk] + case left > 1 { + True -> sized_chunk_loop(rest, count, left - 1, chunk, acc) + False -> + sized_chunk_loop(rest, count, count, [], [reverse(chunk), ..acc]) + } + } + } } /// This function acts similar to fold, but does not take an initial state. @@ -2107,21 +2108,6 @@ pub fn reduce(over list: List(a), with fun: fn(a, a) -> a) -> Result(a, Nil) { } } -fn do_scan( - list: List(a), - accumulator: acc, - accumulated: List(acc), - fun: fn(acc, a) -> acc, -) -> List(acc) { - case list { - [] -> reverse(accumulated) - [first, ..rest] -> { - let next = fun(accumulator, first) - do_scan(rest, next, [next, ..accumulated], fun) - } - } -} - /// Similar to `fold`, but yields the state of the accumulator at each stage. /// /// ## Examples @@ -2136,7 +2122,22 @@ pub fn scan( from initial: acc, with fun: fn(acc, a) -> acc, ) -> List(acc) { - do_scan(list, initial, [], fun) + scan_loop(list, initial, [], fun) +} + +fn scan_loop( + list: List(a), + accumulator: acc, + accumulated: List(acc), + fun: fn(acc, a) -> acc, +) -> List(acc) { + case list { + [] -> reverse(accumulated) + [first, ..rest] -> { + let next = fun(accumulator, first) + scan_loop(rest, next, [next, ..accumulated], fun) + } + } } /// Returns the last element in the given list. @@ -2196,16 +2197,6 @@ pub fn combinations(items: List(a), by n: Int) -> List(List(a)) { } } -fn do_combination_pairs(items: List(a)) -> List(List(#(a, a))) { - case items { - [] -> [] - [first, ..rest] -> { - let first_combinations = map(rest, with: fn(other) { #(first, other) }) - [first_combinations, ..do_combination_pairs(rest)] - } - } -} - /// Return unique pair combinations of elements in the list /// /// ## Examples @@ -2216,10 +2207,20 @@ fn do_combination_pairs(items: List(a)) -> List(List(#(a, a))) { /// ``` /// pub fn combination_pairs(items: List(a)) -> List(#(a, a)) { - do_combination_pairs(items) + combination_pairs_loop(items) |> flatten } +fn combination_pairs_loop(items: List(a)) -> List(List(#(a, a))) { + case items { + [] -> [] + [first, ..rest] -> { + let first_combinations = map(rest, with: fn(other) { #(first, other) }) + [first_combinations, ..combination_pairs_loop(rest)] + } + } +} + /// Make a list alternating the elements from the given lists /// /// ## Examples @@ -2270,22 +2271,6 @@ pub fn transpose(list_of_list: List(List(a))) -> List(List(a)) { } } -fn do_shuffle_pair_unwrap(list: List(#(Float, a)), acc: List(a)) -> List(a) { - case list { - [] -> acc - [elem_pair, ..enumerable] -> - do_shuffle_pair_unwrap(enumerable, [elem_pair.1, ..acc]) - } -} - -fn do_shuffle_by_pair_indexes( - list_of_pairs: List(#(Float, a)), -) -> List(#(Float, a)) { - sort(list_of_pairs, fn(a_pair: #(Float, a), b_pair: #(Float, a)) -> Order { - float.compare(a_pair.0, b_pair.0) - }) -} - /// Takes a list, randomly sorts all items and returns the shuffled list. /// /// This function uses `float.random` to decide the order of the elements. @@ -2301,5 +2286,21 @@ pub fn shuffle(list: List(a)) -> List(a) { list |> fold(from: [], with: fn(acc, a) { [#(float.random(), a), ..acc] }) |> do_shuffle_by_pair_indexes() - |> do_shuffle_pair_unwrap([]) + |> shuffle_pair_unwrap_loop([]) +} + +fn shuffle_pair_unwrap_loop(list: List(#(Float, a)), acc: List(a)) -> List(a) { + case list { + [] -> acc + [elem_pair, ..enumerable] -> + shuffle_pair_unwrap_loop(enumerable, [elem_pair.1, ..acc]) + } +} + +fn do_shuffle_by_pair_indexes( + list_of_pairs: List(#(Float, a)), +) -> List(#(Float, a)) { + sort(list_of_pairs, fn(a_pair: #(Float, a), b_pair: #(Float, a)) -> Order { + float.compare(a_pair.0, b_pair.0) + }) } diff --git a/src/gleam/option.gleam b/src/gleam/option.gleam index 7ada122b..585ec2d1 100644 --- a/src/gleam/option.gleam +++ b/src/gleam/option.gleam @@ -21,21 +21,6 @@ pub type Option(a) { None } -fn do_all(list: List(Option(a)), acc: List(a)) -> Option(List(a)) { - case list { - [] -> Some(acc) - [x, ..rest] -> { - let accumulate = fn(acc, item) { - case acc, item { - Some(values), Some(value) -> Some([value, ..values]) - _, _ -> None - } - } - accumulate(do_all(rest, acc), x) - } - } -} - /// Combines a list of `Option`s into a single `Option`. /// If all elements in the list are `Some` then returns a `Some` holding the list of values. /// If any element is `None` then returns`None`. @@ -53,7 +38,22 @@ fn do_all(list: List(Option(a)), acc: List(a)) -> Option(List(a)) { /// ``` /// pub fn all(list: List(Option(a))) -> Option(List(a)) { - do_all(list, []) + all_loop(list, []) +} + +fn all_loop(list: List(Option(a)), acc: List(a)) -> Option(List(a)) { + case list { + [] -> Some(acc) + [x, ..rest] -> { + let accumulate = fn(acc, item) { + case acc, item { + Some(values), Some(value) -> Some([value, ..values]) + _, _ -> None + } + } + accumulate(all_loop(rest, acc), x) + } + } } /// Checks whether the `Option` is a `Some` value. @@ -328,21 +328,6 @@ pub fn lazy_or(first: Option(a), second: fn() -> Option(a)) -> Option(a) { } } -fn do_values(list: List(Option(a)), acc: List(a)) -> List(a) { - case list { - [] -> acc - [first, ..rest] -> { - let accumulate = fn(acc, item) { - case item { - Some(value) -> [value, ..acc] - None -> acc - } - } - accumulate(do_values(rest, acc), first) - } - } -} - /// Given a list of `Option`s, /// returns only the values inside `Some`. /// @@ -354,5 +339,20 @@ fn do_values(list: List(Option(a)), acc: List(a)) -> List(a) { /// ``` /// pub fn values(options: List(Option(a))) -> List(a) { - do_values(options, []) + values_loop(options, []) +} + +fn values_loop(list: List(Option(a)), acc: List(a)) -> List(a) { + case list { + [] -> acc + [first, ..rest] -> { + let accumulate = fn(acc, item) { + case item { + Some(value) -> [value, ..acc] + None -> acc + } + } + accumulate(values_loop(rest, acc), first) + } + } } diff --git a/src/gleam/result.gleam b/src/gleam/result.gleam index 235c347a..65cae58f 100644 --- a/src/gleam/result.gleam +++ b/src/gleam/result.gleam @@ -380,14 +380,14 @@ pub fn all(results: List(Result(a, e))) -> Result(List(a), e) { /// ``` /// pub fn partition(results: List(Result(a, e))) -> #(List(a), List(e)) { - do_partition(results, [], []) + partition_loop(results, [], []) } -fn do_partition(results: List(Result(a, e)), oks: List(a), errors: List(e)) { +fn partition_loop(results: List(Result(a, e)), oks: List(a), errors: List(e)) { case results { [] -> #(oks, errors) - [Ok(a), ..rest] -> do_partition(rest, [a, ..oks], errors) - [Error(e), ..rest] -> do_partition(rest, oks, [e, ..errors]) + [Ok(a), ..rest] -> partition_loop(rest, [a, ..oks], errors) + [Error(e), ..rest] -> partition_loop(rest, oks, [e, ..errors]) } } diff --git a/src/gleam/string.gleam b/src/gleam/string.gleam index 14acc231..8a3a57e6 100644 --- a/src/gleam/string.gleam +++ b/src/gleam/string.gleam @@ -435,13 +435,13 @@ pub fn concat(strings: List(String)) -> String { /// ``` /// pub fn repeat(string: String, times times: Int) -> String { - do_repeat(string, times, "") + repeat_loop(string, times, "") } -fn do_repeat(string: String, times: Int, acc: String) -> String { +fn repeat_loop(string: String, times: Int, acc: String) -> String { case times <= 0 { True -> acc - False -> do_repeat(string, times - 1, acc <> string) + False -> repeat_loop(string, times - 1, acc <> string) } } @@ -646,13 +646,13 @@ fn do_pop_grapheme(string string: String) -> Result(#(String, String), Nil) /// @external(javascript, "../gleam_stdlib.mjs", "graphemes") pub fn to_graphemes(string: String) -> List(String) { - do_to_graphemes(string, []) + to_graphemes_loop(string, []) |> list.reverse } -fn do_to_graphemes(string: String, acc: List(String)) -> List(String) { +fn to_graphemes_loop(string: String, acc: List(String)) -> List(String) { case pop_grapheme(string) { - Ok(#(grapheme, rest)) -> do_to_graphemes(rest, [grapheme, ..acc]) + Ok(#(grapheme, rest)) -> to_graphemes_loop(rest, [grapheme, ..acc]) _ -> acc } } @@ -693,19 +693,18 @@ pub fn to_utf_codepoints(string: String) -> List(UtfCodepoint) { @target(erlang) fn do_to_utf_codepoints(string: String) -> List(UtfCodepoint) { - do_to_utf_codepoints_impl(<>, []) - |> list.reverse + to_utf_codepoints_loop(<>, []) } @target(erlang) -fn do_to_utf_codepoints_impl( +fn to_utf_codepoints_loop( bit_array: BitArray, acc: List(UtfCodepoint), ) -> List(UtfCodepoint) { case bit_array { <> -> - do_to_utf_codepoints_impl(rest, [first, ..acc]) - _ -> acc + to_utf_codepoints_loop(rest, [first, ..acc]) + _ -> list.reverse(acc) } }