aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2021-07-22 21:44:57 +0100
committerLouis Pilfold <louis@lpil.uk>2021-07-22 21:44:57 +0100
commit8d56e443e1df58a320629dfeae42270f9e7ef4da (patch)
treeb4e92d91324a3a622553d247f44f8030310c4839
parent0cca8008dd81ee5cf4086bfd28280b2f398096c7 (diff)
downloadgleam_stdlib-8d56e443e1df58a320629dfeae42270f9e7ef4da.tar.gz
gleam_stdlib-8d56e443e1df58a320629dfeae42270f9e7ef4da.zip
Convert more string functions
-rw-r--r--src/gleam/bit_builder.gleam28
-rw-r--r--src/gleam/iterator.gleam1555
-rw-r--r--src/gleam/string.gleam494
-rw-r--r--src/gleam/string_builder.gleam4
-rw-r--r--src/gleam_stdlib.erl2
-rw-r--r--src/gleam_stdlib.js85
-rw-r--r--test/gleam/string_test.gleam354
7 files changed, 1351 insertions, 1171 deletions
diff --git a/src/gleam/bit_builder.gleam b/src/gleam/bit_builder.gleam
index 738c79c..f3cf026 100644
--- a/src/gleam/bit_builder.gleam
+++ b/src/gleam/bit_builder.gleam
@@ -20,25 +20,25 @@ if erlang {
///
/// Runs in constant time.
///
- pub external fn prepend(to: BitBuilder, prefix: BitString) -> BitBuilder =
- "gleam_stdlib" "iodata_prepend"
+ pub fn prepend(to: BitBuilder, prefix: BitString) -> BitBuilder {
+ append_builder(from_bit_string(prefix), to)
+ }
/// Appends a bit string to the end of a builder.
///
/// Runs in constant time.
///
- pub external fn append(to: BitBuilder, suffix: BitString) -> BitBuilder =
- "gleam_stdlib" "iodata_append"
+ pub fn append(to: BitBuilder, suffix: BitString) -> BitBuilder {
+ append_builder(to, from_bit_string(suffix))
+ }
/// Prepends a builder onto the start of another.
///
/// Runs in constant time.
///
- pub external fn prepend_builder(
- to: BitBuilder,
- prefix: BitBuilder,
- ) -> BitBuilder =
- "gleam_stdlib" "iodata_prepend"
+ pub fn prepend_builder(to: BitBuilder, prefix: BitBuilder) -> BitBuilder {
+ append_builder(prefix, to)
+ }
/// Appends a builder onto the end of another.
///
@@ -54,15 +54,17 @@ if erlang {
///
/// Runs in constant time.
///
- pub external fn prepend_string(to: BitBuilder, prefix: String) -> BitBuilder =
- "gleam_stdlib" "iodata_prepend"
+ pub fn prepend_string(to: BitBuilder, prefix: String) -> BitBuilder {
+ append_builder(from_string(prefix), to)
+ }
/// Appends a string onto the end of a builder.
///
/// Runs in constant time.
///
- pub external fn append_string(to: BitBuilder, suffix: String) -> BitBuilder =
- "gleam_stdlib" "iodata_append"
+ pub fn append_string(to: BitBuilder, suffix: String) -> BitBuilder {
+ append_builder(to, from_string(suffix))
+ }
/// Joins a list of builders into a single builders.
///
diff --git a/src/gleam/iterator.gleam b/src/gleam/iterator.gleam
index fc296e9..e7405bb 100644
--- a/src/gleam/iterator.gleam
+++ b/src/gleam/iterator.gleam
@@ -1,892 +1,889 @@
+import gleam/list
+
if erlang {
- import gleam/list
- import gleam/map.{Map}
import gleam/option.{None, Option, Some}
+ import gleam/map.{Map}
+}
- // Internal private representation of an Iterator
- type Action(element) {
- Stop
- Continue(element, fn() -> Action(element))
- }
+// Internal private representation of an Iterator
+type Action(element) {
+ Stop
+ Continue(element, fn() -> Action(element))
+}
- /// An iterator is a lazily evaluated sequence of element.
- ///
- /// Iterators are useful when working with collections that are too large to
- /// fit in memory (or those that are infinite in size) as they only require the
- /// elements currently being processed to be in memory.
- ///
- /// As a lazy data structure no work is done when an iterator is filters,
- /// mapped, etc, instead a new iterator is returned with these transformations
- /// applied to the stream. Once the stream has all the required transformations
- /// applied it can be evaluated using functions such as `fold` and `to_list`.
- ///
- pub opaque type Iterator(element) {
- Iterator(continuation: fn() -> Action(element))
- }
+/// An iterator is a lazily evaluated sequence of element.
+///
+/// Iterators are useful when working with collections that are too large to
+/// fit in memory (or those that are infinite in size) as they only require the
+/// elements currently being processed to be in memory.
+///
+/// As a lazy data structure no work is done when an iterator is filters,
+/// mapped, etc, instead a new iterator is returned with these transformations
+/// applied to the stream. Once the stream has all the required transformations
+/// applied it can be evaluated using functions such as `fold` and `to_list`.
+///
+pub opaque type Iterator(element) {
+ Iterator(continuation: fn() -> Action(element))
+}
- // Public API for iteration
- pub type Step(element, accumulator) {
- Next(element: element, accumulator: accumulator)
- Done
- }
+// Public API for iteration
+pub type Step(element, accumulator) {
+ Next(element: element, accumulator: accumulator)
+ Done
+}
- // Shortcut for an empty iterator.
- fn stop() -> Action(element) {
- Stop
- }
+// Shortcut for an empty iterator.
+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
- }
+// 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`,
- /// indicating the iterator has no more elements, or `Next` which contains a
- /// new element and accumulator. The element is yielded by the iterator and the
- /// new accumulator is used with the function to compute the next element in
- /// the sequence.
- ///
- /// ## Examples
- ///
- /// > unfold(from: 5, with: fn(n) {
- /// > case n {
- /// > 0 -> Done
- /// > n -> Next(element: n, accumulator: n - 1)
- /// > }
- /// > })
- /// > |> to_list
- /// [5, 4, 3, 2, 1]
- ///
- pub fn unfold(
- from initial: acc,
- with f: fn(acc) -> Step(element, acc),
- ) -> Iterator(element) {
- initial
- |> do_unfold(f)
- |> Iterator
- }
+/// Creates an iterator from a given function and accumulator.
+///
+/// The function is called on the accumulator and returns either `Done`,
+/// indicating the iterator has no more elements, or `Next` which contains a
+/// new element and accumulator. The element is yielded by the iterator and the
+/// new accumulator is used with the function to compute the next element in
+/// the sequence.
+///
+/// ## Examples
+///
+/// > unfold(from: 5, with: fn(n) {
+/// > case n {
+/// > 0 -> Done
+/// > n -> Next(element: n, accumulator: n - 1)
+/// > }
+/// > })
+/// > |> to_list
+/// [5, 4, 3, 2, 1]
+///
+pub fn unfold(
+ from initial: acc,
+ with f: fn(acc) -> Step(element, acc),
+) -> Iterator(element) {
+ initial
+ |> do_unfold(f)
+ |> Iterator
+}
- // TODO: test
- /// Creates an iterator that yields values created by calling a given function
- /// repeatedly.
- ///
- pub fn repeatedly(f: fn() -> element) -> Iterator(element) {
- unfold(Nil, fn(_) { Next(f(), Nil) })
- }
+// TODO: test
+/// Creates an iterator that yields values created by calling a given function
+/// repeatedly.
+///
+pub fn repeatedly(f: fn() -> element) -> Iterator(element) {
+ unfold(Nil, fn(_) { Next(f(), Nil) })
+}
- /// Creates an iterator that returns the same value infinitely.
- ///
- /// ## Examples
- ///
- /// > repeat(10)
- /// > |> take(4)
- /// > |> to_list
- /// [10, 10, 10, 10]
- ///
- pub fn repeat(x: element) -> Iterator(element) {
- repeatedly(fn() { x })
- }
+/// Creates an iterator that returns the same value infinitely.
+///
+/// ## Examples
+///
+/// > repeat(10)
+/// > |> take(4)
+/// > |> to_list
+/// [10, 10, 10, 10]
+///
+pub fn repeat(x: element) -> Iterator(element) {
+ repeatedly(fn() { x })
+}
- /// Creates an iterator that yields each element from the given list.
- ///
- /// ## Examples
- ///
- /// > from_list([1, 2, 3, 4]) |> to_list
- /// [1, 2, 3, 4]
- ///
- pub fn from_list(list: List(element)) -> Iterator(element) {
- let yield = fn(acc) {
- case acc {
- [] -> Done
- [head, ..tail] -> Next(head, tail)
- }
+/// Creates an iterator that yields each element from the given list.
+///
+/// ## Examples
+///
+/// > from_list([1, 2, 3, 4]) |> to_list
+/// [1, 2, 3, 4]
+///
+pub fn from_list(list: List(element)) -> Iterator(element) {
+ let yield = fn(acc) {
+ case acc {
+ [] -> Done
+ [head, ..tail] -> Next(head, tail)
}
- unfold(list, yield)
}
+ unfold(list, yield)
+}
- // Consuming Iterators
- fn do_fold(
- continuation: fn() -> Action(e),
- f: fn(e, acc) -> acc,
- accumulator: acc,
- ) -> acc {
- case continuation() {
- Continue(elem, next) -> do_fold(next, f, f(elem, accumulator))
- Stop -> accumulator
- }
+// Consuming Iterators
+fn do_fold(
+ continuation: fn() -> Action(e),
+ f: fn(e, acc) -> acc,
+ accumulator: acc,
+) -> acc {
+ case continuation() {
+ Continue(elem, next) -> do_fold(next, f, f(elem, accumulator))
+ Stop -> accumulator
}
+}
- /// Reduces an iterator of elements into a single value by calling a given
- /// function on each element in turn.
- ///
- /// If called on an iterator of infinite length then this function will never
- /// return.
- ///
- /// If you do not care about the end value and only wish to evaluate the
- /// iterator for side effects consider using the `run` function instead.
- ///
- /// ## Examples
- ///
- /// > [1, 2, 3, 4]
- /// > |> from_list
- /// > |> fold(from: 0, with: fn(element, acc) { element + acc })
- /// 10
- ///
- pub fn fold(
- over iterator: Iterator(e),
- from initial: acc,
- with f: fn(e, acc) -> acc,
- ) -> acc {
- iterator.continuation
- |> do_fold(f, initial)
- }
+/// Reduces an iterator of elements into a single value by calling a given
+/// function on each element in turn.
+///
+/// If called on an iterator of infinite length then this function will never
+/// return.
+///
+/// If you do not care about the end value and only wish to evaluate the
+/// iterator for side effects consider using the `run` function instead.
+///
+/// ## Examples
+///
+/// > [1, 2, 3, 4]
+/// > |> from_list
+/// > |> fold(from: 0, with: fn(element, acc) { element + acc })
+/// 10
+///
+pub fn fold(
+ over iterator: Iterator(e),
+ from initial: acc,
+ with f: fn(e, acc) -> acc,
+) -> acc {
+ iterator.continuation
+ |> do_fold(f, initial)
+}
- // TODO: test
- /// Evaluates all elements emitted by the given iterator. This function is useful for when
- /// you wish to trigger any side effects that would occur when evaluating
- /// the iterator.
- ///
- pub fn run(iterator: Iterator(e)) -> Nil {
- fold(iterator, Nil, fn(_, _) { Nil })
- }
+// TODO: test
+/// Evaluates all elements emitted by the given iterator. This function is useful for when
+/// you wish to trigger any side effects that would occur when evaluating
+/// the iterator.
+///
+pub fn run(iterator: Iterator(e)) -> Nil {
+ fold(iterator, Nil, fn(_, _) { Nil })
+}
- /// Evaluates an iterator and returns all the elements as a list.
- ///
- /// If called on an iterator of infinite length then this function will never
- /// return.
- ///
- /// ## Examples
- ///
- /// > [1, 2, 3] |> from_list |> map(fn(x) { x * 2 }) |> to_list
- /// [2, 4, 6]
- ///
- pub fn to_list(iterator: Iterator(element)) -> List(element) {
- iterator
- |> fold([], fn(e, acc) { [e, ..acc] })
- |> list.reverse
+/// Evaluates an iterator and returns all the elements as a list.
+///
+/// If called on an iterator of infinite length then this function will never
+/// return.
+///
+/// ## Examples
+///
+/// > [1, 2, 3] |> from_list |> map(fn(x) { x * 2 }) |> to_list
+/// [2, 4, 6]
+///
+pub fn to_list(iterator: Iterator(element)) -> List(element) {
+ iterator
+ |> fold([], fn(e, acc) { [e, ..acc] })
+ |> list.reverse
+}
+
+/// Eagerly accesses the first value of an interator, returning a `Next`
+/// that contains the first value and the rest of the iterator.
+///
+/// If called on an empty iterator, `Done` is returned.
+///
+/// ## Examples
+///
+/// > assert Next(head, tail) =
+/// > [1, 2, 3, 4]
+/// > |> from_list
+/// > |> step
+/// > head
+/// 1
+///
+/// > tail |> to_list
+/// [2, 3, 4]
+///
+/// > empty() |> step
+/// Done
+///
+pub fn step(iterator: Iterator(e)) -> Step(e, Iterator(e)) {
+ case iterator.continuation() {
+ Stop -> Done
+ Continue(e, a) -> Next(e, Iterator(a))
}
+}
- /// Eagerly accesses the first value of an interator, returning a `Next`
- /// that contains the first value and the rest of the iterator.
- ///
- /// If called on an empty iterator, `Done` is returned.
- ///
- /// ## Examples
- ///
- /// > assert Next(head, tail) =
- /// > [1, 2, 3, 4]
- /// > |> from_list
- /// > |> step
- /// > head
- /// 1
- ///
- /// > tail |> to_list
- /// [2, 3, 4]
- ///
- /// > empty() |> step
- /// Done
- ///
- pub fn step(iterator: Iterator(e)) -> Step(e, Iterator(e)) {
- case iterator.continuation() {
- Stop -> Done
- Continue(e, a) -> Next(e, Iterator(a))
+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))
+ }
}
}
+}
- fn do_take(continuation: fn() -> Action(e), desired: Int) -> fn() -> Action(e) {
- fn() {
+/// Creates an iterator that only yields the first `desired` elements.
+///
+/// If the iterator does not have enough elements all of them are yielded.
+///
+/// ## Examples
+///
+/// > [1, 2, 3, 4, 5] |> from_list |> take(up_to: 3) |> to_list
+/// [1, 2, 3]
+///
+/// > [1, 2] |> from_list |> take(up_to: 3) |> to_list
+/// [1, 2]
+///
+pub fn take(from iterator: Iterator(e), up_to desired: Int) -> Iterator(e) {
+ iterator.continuation
+ |> do_take(desired)
+ |> Iterator
+}
+
+fn do_drop(continuation: fn() -> Action(e), desired: Int) -> Action(e) {
+ case continuation() {
+ Stop -> Stop
+ Continue(e, next) ->
case desired > 0 {
- False -> Stop
- True ->
- case continuation() {
- Stop -> Stop
- Continue(e, next) -> Continue(e, do_take(next, desired - 1))
- }
+ True -> do_drop(next, desired - 1)
+ False -> Continue(e, next)
}
- }
}
+}
- /// Creates an iterator that only yields the first `desired` elements.
- ///
- /// If the iterator does not have enough elements all of them are yielded.
- ///
- /// ## Examples
- ///
- /// > [1, 2, 3, 4, 5] |> from_list |> take(up_to: 3) |> to_list
- /// [1, 2, 3]
- ///
- /// > [1, 2] |> from_list |> take(up_to: 3) |> to_list
- /// [1, 2]
- ///
- pub fn take(from iterator: Iterator(e), up_to desired: Int) -> Iterator(e) {
- iterator.continuation
- |> do_take(desired)
- |> Iterator
- }
+/// Evaluates and discards the first N elements in an iterator, returning a new
+/// iterator.
+///
+/// If the iterator does not have enough elements an empty iterator is
+/// returned.
+///
+/// This function does not evaluate the elements of the iterator, the
+/// computation is performed when the iterator is later run.
+///
+/// ## Examples
+///
+/// > [1, 2, 3, 4, 5] |> from_list |> drop(up_to: 3) |> to_list
+/// [4, 5]
+///
+/// > [1, 2] |> from_list |> drop(up_to: 3) |> to_list
+/// []
+///
+pub fn drop(from iterator: Iterator(e), up_to desired: Int) -> Iterator(e) {
+ fn() { do_drop(iterator.continuation, desired) }
+ |> Iterator
+}
- fn do_drop(continuation: fn() -> Action(e), desired: Int) -> Action(e) {
+fn do_map(continuation: fn() -> Action(a), f: fn(a) -> b) -> fn() -> Action(b) {
+ fn() {
case continuation() {
Stop -> Stop
- Continue(e, next) ->
- case desired > 0 {
- True -> do_drop(next, desired - 1)
- False -> Continue(e, next)
- }
+ Continue(e, continuation) -> Continue(f(e), do_map(continuation, f))
}
}
+}
- /// Evaluates and discards the first N elements in an iterator, returning a new
- /// iterator.
- ///
- /// If the iterator does not have enough elements an empty iterator is
- /// returned.
- ///
- /// This function does not evaluate the elements of the iterator, the
- /// computation is performed when the iterator is later run.
- ///
- /// ## Examples
- ///
- /// > [1, 2, 3, 4, 5] |> from_list |> drop(up_to: 3) |> to_list
- /// [4, 5]
- ///
- /// > [1, 2] |> from_list |> drop(up_to: 3) |> to_list
- /// []
- ///
- pub fn drop(from iterator: Iterator(e), up_to desired: Int) -> Iterator(e) {
- fn() { do_drop(iterator.continuation, desired) }
- |> Iterator
- }
+/// Creates an iterator from an existing iterator and a transformation function.
+///
+/// Each element in the new iterator will be the result of calling the given
+/// function on the elements in the given iterator.
+///
+/// This function does not evaluate the elements of the iterator, the
+/// computation is performed when the iterator is later run.
+///
+/// ## Examples
+///
+/// > [1, 2, 3] |> from_list |> map(fn(x) { x * 2 }) |> to_list
+/// [2, 4, 6]
+///
+pub fn map(over iterator: Iterator(a), with f: fn(a) -> b) -> Iterator(b) {
+ iterator.continuation
+ |> do_map(f)
+ |> 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 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()
}
+}
- /// Creates an iterator from an existing iterator and a transformation function.
- ///
- /// Each element in the new iterator will be the result of calling the given
- /// function on the elements in the given iterator.
- ///
- /// This function does not evaluate the elements of the iterator, the
- /// computation is performed when the iterator is later run.
- ///
- /// ## Examples
- ///
- /// > [1, 2, 3] |> from_list |> map(fn(x) { x * 2 }) |> to_list
- /// [2, 4, 6]
- ///
- pub fn map(over iterator: Iterator(a), with f: fn(a) -> b) -> Iterator(b) {
- iterator.continuation
- |> do_map(f)
- |> Iterator
- }
+/// Appends two iterators, producing a new iterator.
+///
+/// This function does not evaluate the elements of the iterators, the
+/// computation is performed when the resulting iterator is later run.
+///
+/// ## Examples
+///
+/// > [1, 2] |> from_list |> append([3, 4] |> from_list) |> to_list
+/// [1, 2, 3, 4]
+///
+pub fn append(to first: Iterator(a), suffix second: Iterator(a)) -> Iterator(a) {
+ fn() { do_append(first.continuation, second.continuation) }
+ |> 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 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) })
}
+}
- /// Appends two iterators, producing a new iterator.
- ///
- /// This function does not evaluate the elements of the iterators, the
- /// computation is performed when the resulting iterator is later run.
- ///
- /// ## Examples
- ///
- /// > [1, 2] |> from_list |> append([3, 4] |> from_list) |> to_list
- /// [1, 2, 3, 4]
- ///
- pub fn append(
- to first: Iterator(a),
- suffix second: Iterator(a),
- ) -> Iterator(a) {
- fn() { do_append(first.continuation, second.continuation) }
- |> Iterator
- }
+/// Flattens an iterator of iterators, creating a new iterator.
+///
+/// This function does not evaluate the elements of the iterator, the
+/// computation is performed when the iterator is later run.
+///
+/// ## Examples
+///
+/// > from_list([[1, 2], [3, 4]]) |> map(from_list) |> flatten |> to_list
+/// [1, 2, 3, 4]
+///
+pub fn flatten(iterator: Iterator(Iterator(a))) -> Iterator(a) {
+ fn() { do_flatten(iterator.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) })
- }
- }
+/// Creates an iterator from an existing iterator and a transformation function.
+///
+/// Each element in the new iterator will be the result of calling the given
+/// function on the elements in the given iterator and then flattening the
+/// results.
+///
+/// This function does not evaluate the elements of the iterator, the
+/// computation is performed when the iterator is later run.
+///
+/// ## Examples
+///
+/// > [1, 2] |> from_list |> flat_map(fn(x) { from_list([x, x + 1]) }) |> to_list
+/// [1, 2, 2, 3]
+///
+pub fn flat_map(
+ over iterator: Iterator(a),
+ with f: fn(a) -> Iterator(b),
+) -> Iterator(b) {
+ iterator
+ |> map(f)
+ |> flatten
+}
- /// Flattens an iterator of iterators, creating a new iterator.
- ///
- /// This function does not evaluate the elements of the iterator, the
- /// computation is performed when the iterator is later run.
- ///
- /// ## Examples
- ///
- /// > from_list([[1, 2], [3, 4]]) |> map(from_list) |> flatten |> to_list
- /// [1, 2, 3, 4]
- ///
- pub fn flatten(iterator: Iterator(Iterator(a))) -> Iterator(a) {
- fn() { do_flatten(iterator.continuation) }
- |> Iterator
+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 transformation function.
- ///
- /// Each element in the new iterator will be the result of calling the given
- /// function on the elements in the given iterator and then flattening the
- /// results.
- ///
- /// This function does not evaluate the elements of the iterator, the
- /// computation is performed when the iterator is later run.
- ///
- /// ## Examples
- ///
- /// > [1, 2] |> from_list |> flat_map(fn(x) { from_list([x, x + 1]) }) |> to_list
- /// [1, 2, 2, 3]
- ///
- pub fn flat_map(
- over iterator: Iterator(a),
- with f: fn(a) -> Iterator(b),
- ) -> Iterator(b) {
- iterator
- |> map(f)
- |> flatten
- }
+/// Creates an iterator from an existing iterator and a predicate function.
+///
+/// The new iterator will contain elements from the first iterator for which
+/// the given function returns `True`.
+///
+/// This function does not evaluate the elements of the iterator, the
+/// computation is performed when the iterator is later run.
+///
+/// ## Examples
+///
+/// > import gleam/int
+/// > [1, 2, 3, 4] |> from_list |> filter(int.is_even) |> to_list
+/// [2, 4]
+///
+pub fn filter(
+ iterator: Iterator(a),
+ for predicate: fn(a) -> Bool,
+) -> Iterator(a) {
+ fn() { do_filter(iterator.continuation, predicate) }
+ |> Iterator
+}
- 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 that repeats a given iterator infinitely.
+///
+/// ## Examples
+///
+/// > [1, 2] |> from_list |> cycle |> take(6)
+/// [1, 2, 1, 2, 1, 2]
+///
+pub fn cycle(iterator: Iterator(a)) -> Iterator(a) {
+ repeat(iterator)
+ |> flatten
+}
+
+/// Creates an iterator of ints, starting at a given start int and stepping by
+/// one to a given end int.
+///
+/// ## Examples
+///
+/// > range(from: 1, to: 5) |> to_list
+/// [1, 2, 3, 4]
+///
+/// > range(from: 1, to: -2) |> to_list
+/// [1, 0, -1]
+///
+/// > range(from: 0, to: 0) |> to_list
+/// []
+///
+pub fn range(from start: Int, to stop: Int) -> Iterator(Int) {
+ let increment = case start < stop {
+ True -> 1
+ False -> -1
+ }
+
+ let next_step = fn(current) {
+ case current == stop {
+ True -> Done
+ False -> Next(current, current + increment)
}
}
- /// Creates an iterator from an existing iterator and a predicate function.
- ///
- /// The new iterator will contain elements from the first iterator for which
- /// the given function returns `True`.
- ///
- /// This function does not evaluate the elements of the iterator, the
- /// computation is performed when the iterator is later run.
- ///
- /// ## Examples
- ///
- /// > import gleam/int
- /// > [1, 2, 3, 4] |> from_list |> filter(int.is_even) |> to_list
- /// [2, 4]
- ///
- pub fn filter(
- iterator: Iterator(a),
- for predicate: fn(a) -> Bool,
- ) -> Iterator(a) {
- fn() { do_filter(iterator.continuation, predicate) }
- |> Iterator
- }
+ unfold(start, next_step)
+}
- /// Creates an iterator that repeats a given iterator infinitely.
- ///
- /// ## Examples
- ///
- /// > [1, 2] |> from_list |> cycle |> take(6)
- /// [1, 2, 1, 2, 1, 2]
- ///
- pub fn cycle(iterator: Iterator(a)) -> Iterator(a) {
- repeat(iterator)
- |> flatten
+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)
+ }
}
+}
- /// Creates an iterator of ints, starting at a given start int and stepping by
- /// one to a given end int.
- ///
- /// ## Examples
- ///
- /// > range(from: 1, to: 5) |> to_list
- /// [1, 2, 3, 4]
- ///
- /// > range(from: 1, to: -2) |> to_list
- /// [1, 0, -1]
- ///
- /// > range(from: 0, to: 0) |> to_list
- /// []
- ///
- pub fn range(from start: Int, to stop: Int) -> Iterator(Int) {
- let increment = case start < stop {
- True -> 1
- False -> -1
- }
+/// Finds the first element in a given iterator for which the given function returns
+/// True.
+///
+/// Returns `Error(Nil)` if the function does not return True for any of the
+/// elements.
+///
+/// ## Examples
+///
+/// > find(from_list([1, 2, 3]), fn(x) { x > 2 })
+/// Ok(3)
+///
+/// > find(from_list([1, 2, 3]), fn(x) { x > 4 })
+/// Error(Nil)
+///
+/// > find(empty(), fn(x) { True })
+/// Error(Nil)
+///
+pub fn find(
+ in haystack: Iterator(a),
+ one_that is_desired: fn(a) -> Bool,
+) -> Result(a, Nil) {
+ haystack.continuation
+ |> do_find(is_desired)
+}
- let next_step = fn(current) {
- case current == stop {
- True -> Done
- False -> Next(current, current + increment)
- }
+fn do_index(
+ continuation: fn() -> Action(element),
+ next: Int,
+) -> fn() -> Action(#(Int, element)) {
+ fn() {
+ case continuation() {
+ Stop -> Stop
+ Continue(e, continuation) ->
+ Continue(#(next, e), do_index(continuation, next + 1))
}
-
- unfold(start, next_step)
}
+}
+
+/// Wraps values yielded from an iterator with indices, starting from 0.
+///
+/// ## Examples
+///
+/// > from_list(["a", "b", "c"]) |> index |> to_list
+/// [#(0, "a"), #(1, "b"), #(2, "c")]
+///
+pub fn index(over iterator: Iterator(element)) -> Iterator(#(Int, element)) {
+ iterator.continuation
+ |> do_index(0)
+ |> Iterator
+}
+
+/// Creates an iterator that inifinitely applies a function to a value.
+///
+/// ## Examples
+///
+/// > iterate(1, fn(n) { n * 3 }) |> take(5)
+/// [1, 3, 9, 27, 81]
+///
+pub fn iterate(
+ from initial: element,
+ with f: fn(element) -> element,
+) -> Iterator(element) {
+ unfold(initial, fn(element) { Next(element, f(element)) })
+}
- fn do_find(
- continuation: fn() -> Action(a),
- f: fn(a) -> Bool,
- ) -> Result(a, Nil) {
+fn do_take_while(
+ continuation: fn() -> Action(element),
+ predicate: fn(element) -> Bool,
+) -> fn() -> Action(element) {
+ fn() {
case continuation() {
- Stop -> Error(Nil)
+ Stop -> Stop
Continue(e, next) ->
- case f(e) {
- True -> Ok(e)
- False -> do_find(next, f)
+ case predicate(e) {
+ False -> Stop
+ True -> Continue(e, do_take_while(next, predicate))
}
}
}
+}
- /// Finds the first element in a given iterator for which the given function returns
- /// True.
- ///
- /// Returns `Error(Nil)` if the function does not return True for any of the
- /// elements.
- ///
- /// ## Examples
- ///
- /// > find(from_list([1, 2, 3]), fn(x) { x > 2 })
- /// Ok(3)
- ///
- /// > find(from_list([1, 2, 3]), fn(x) { x > 4 })
- /// Error(Nil)
- ///
- /// > find(empty(), fn(x) { True })
- /// Error(Nil)
- ///
- pub fn find(
- in haystack: Iterator(a),
- one_that is_desired: fn(a) -> Bool,
- ) -> Result(a, Nil) {
- haystack.continuation
- |> do_find(is_desired)
- }
+/// Creates an iterator that yields elements while the predicate returns `True`.
+///
+/// ## Examples
+///
+/// > from_list([1, 2, 3, 2, 4]) |> take_while(satisfying: fn(x) { x < 3 }) |> to_list
+/// [1, 2]
+///
+pub fn take_while(
+ in iterator: Iterator(element),
+ satisfying predicate: fn(element) -> Bool,
+) -> Iterator(element) {
+ iterator.continuation
+ |> do_take_while(predicate)
+ |> Iterator
+}
- fn do_index(
- continuation: fn() -> Action(element),
- next: Int,
- ) -> fn() -> Action(#(Int, element)) {
- fn() {
- case continuation() {
- Stop -> Stop
- Continue(e, continuation) ->
- Continue(#(next, e), do_index(continuation, next + 1))
+fn do_drop_while(
+ 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)
}
- }
- }
-
- /// Wraps values yielded from an iterator with indices, starting from 0.
- ///
- /// ## Examples
- ///
- /// > from_list(["a", "b", "c"]) |> index |> to_list
- /// [#(0, "a"), #(1, "b"), #(2, "c")]
- ///
- pub fn index(over iterator: Iterator(element)) -> Iterator(#(Int, element)) {
- iterator.continuation
- |> do_index(0)
- |> Iterator
}
+}
- /// Creates an iterator that inifinitely applies a function to a value.
- ///
- /// ## Examples
- ///
- /// > iterate(1, fn(n) { n * 3 }) |> take(5)
- /// [1, 3, 9, 27, 81]
- ///
- pub fn iterate(
- from initial: element,
- with f: fn(element) -> element,
- ) -> Iterator(element) {
- unfold(initial, fn(element) { Next(element, f(element)) })
- }
+/// Creates an iterator that drops elements while the predicate returns `True`,
+/// and then yields the remaining elements.
+///
+/// ## Examples
+///
+/// > from_list([1, 2, 3, 4, 2, 5]) |> drop_while(satisfying: fn(x) { x < 4 }) |> to_list
+/// [4, 2, 5]
+///
+pub fn drop_while(
+ in iterator: Iterator(element),
+ satisfying predicate: fn(element) -> Bool,
+) -> Iterator(element) {
+ fn() { do_drop_while(iterator.continuation, predicate) }
+ |> Iterator
+}
- 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))
- }
+fn do_scan(
+ continuation: fn() -> Action(element),
+ f: fn(element, acc) -> acc,
+ accumulator: acc,
+) -> fn() -> Action(acc) {
+ fn() {
+ case continuation() {
+ Stop -> Stop
+ Continue(el, next) -> {
+ let accumulated = f(el, accumulator)
+ Continue(accumulated, do_scan(next, f, accumulated))
}
}
}
+}
- /// Creates an iterator that yields elements while the predicate returns `True`.
- ///
- /// ## Examples
- ///
- /// > from_list([1, 2, 3, 2, 4]) |> take_while(satisfying: fn(x) { x < 3 }) |> to_list
- /// [1, 2]
- ///
- pub fn take_while(
- in iterator: Iterator(element),
- satisfying predicate: fn(element) -> Bool,
- ) -> Iterator(element) {
- iterator.continuation
- |> do_take_while(predicate)
- |> Iterator
- }
+/// Creates an iterator from an existing iterator and a stateful function.
+///
+/// Specifically, this behaves like `fold`, but yields intermediate results.
+///
+/// ## Examples
+///
+/// Generate a sequence of partial sums:
+/// > from_list([1, 2, 3, 4, 5]) |> scan(from: 0, with: fn(el, acc) { acc + el }) |> to_list
+/// [1, 3, 6, 10, 15]
+///
+pub fn scan(
+ over iterator: Iterator(element),
+ from initial: acc,
+ with f: fn(element, acc) -> acc,
+) -> Iterator(acc) {
+ iterator.continuation
+ |> do_scan(f, initial)
+ |> Iterator
+}
- fn do_drop_while(
- continuation: fn() -> Action(element),
- predicate: fn(element) -> Bool,
- ) -> Action(element) {
- case continuation() {
+fn do_zip(
+ left: fn() -> Action(a),
+ right: fn() -> Action(b),
+) -> fn() -> Action(#(a, b)) {
+ fn() {
+ case left() {
Stop -> Stop
- Continue(e, next) ->
- case predicate(e) {
- False -> Continue(e, next)
- True -> do_drop_while(next, predicate)
+ 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))
}
}
}
+}
- /// Creates an iterator that drops elements while the predicate returns `True`,
- /// and then yields the remaining elements.
- ///
- /// ## Examples
- ///
- /// > from_list([1, 2, 3, 4, 2, 5]) |> drop_while(satisfying: fn(x) { x < 4 }) |> to_list
- /// [4, 2, 5]
- ///
- pub fn drop_while(
- in iterator: Iterator(element),
- satisfying predicate: fn(element) -> Bool,
- ) -> Iterator(element) {
- fn() { do_drop_while(iterator.continuation, predicate) }
- |> Iterator
- }
-
- fn do_scan(
- continuation: fn() -> Action(element),
- f: fn(element, acc) -> acc,
- accumulator: acc,
- ) -> fn() -> Action(acc) {
- fn() {
- case continuation() {
- Stop -> Stop
- Continue(el, next) -> {
- let accumulated = f(el, accumulator)
- Continue(accumulated, do_scan(next, f, accumulated))
- }
- }
- }
- }
+/// Zips two iterators together, emitting values from both
+/// until the shorter one runs out.
+///
+/// ## Examples
+///
+/// > from_list(["a", "b", "c"]) |> zip(range(20, 30)) |> to_list
+/// [#("a", 20), #("b", 21), #("c", 22)]
+///
+pub fn zip(left: Iterator(a), right: Iterator(b)) -> Iterator(#(a, b)) {
+ do_zip(left.continuation, right.continuation)
+ |> Iterator
+}
- /// Creates an iterator from an existing iterator and a stateful function.
- ///
- /// Specifically, this behaves like `fold`, but yields intermediate results.
- ///
- /// ## Examples
- ///
- /// Generate a sequence of partial sums:
- /// > from_list([1, 2, 3, 4, 5]) |> scan(from: 0, with: fn(el, acc) { acc + el }) |> to_list
- /// [1, 3, 6, 10, 15]
- ///
- pub fn scan(
- over iterator: Iterator(element),
- from initial: acc,
- with f: fn(element, acc) -> acc,
- ) -> Iterator(acc) {
- iterator.continuation
- |> do_scan(f, initial)
- |> 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 do_zip(
- 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), do_zip(next_left, next_right))
- }
+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)
}
}
}
+}
- /// Zips two iterators together, emitting values from both
- /// until the shorter one runs out.
- ///
- /// ## Examples
- ///
- /// > from_list(["a", "b", "c"]) |> zip(range(20, 30)) |> to_list
- /// [#("a", 20), #("b", 21), #("c", 22)]
- ///
- pub fn zip(left: Iterator(a), right: Iterator(b)) -> Iterator(#(a, b)) {
- do_zip(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 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) })
}
+}
- 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)
- }
- }
+/// Creates an iterator that emits chunks of elements
+/// for which `f` returns the same value.
+///
+/// ## Examples
+///
+/// > from_list([1, 2, 2, 3, 4, 4, 6, 7, 7]) |> chunk(by: fn(n) { n % 2 }) |> to_list
+/// [[1], [2, 2], [3], [4, 4, 6], [7, 7]]
+///
+pub fn chunk(
+ over iterator: Iterator(element),
+ by f: fn(element) -> key,
+) -> Iterator(List(element)) {
+ fn() {
+ case iterator.continuation() {
+ Stop -> Stop
+ Continue(e, next) -> do_chunk(next, f, f(e), e)
}
}
+ |> Iterator
+}
- 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 sized chunk
+type SizedChunk(element) {
+ Another(List(element), fn() -> Action(element))
+ Last(List(element))
+ NoMore
+}
- /// Creates an iterator that emits chunks of elements
- /// for which `f` returns the same value.
- ///
- /// ## Examples
- ///
- /// > from_list([1, 2, 2, 3, 4, 4, 6, 7, 7]) |> chunk(by: fn(n) { n % 2 }) |> to_list
- /// [[1], [2, 2], [3], [4, 4, 6], [7, 7]]
- ///
- pub fn chunk(
- over iterator: Iterator(element),
- by f: fn(element) -> key,
- ) -> Iterator(List(element)) {
- fn() {
- case iterator.continuation() {
- Stop -> Stop
- Continue(e, next) -> do_chunk(next, f, f(e), e)
+fn next_sized_chunk(
+ continuation: fn() -> Action(element),
+ left: Int,
+ current_chunk: List(element),
+) -> SizedChunk(element) {
+ case continuation() {
+ Stop ->
+ case current_chunk {
+ [] -> NoMore
+ remaining -> Last(list.reverse(remaining))
}
- }
- |> Iterator
- }
-
- // 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 ->
- case current_chunk {
- [] -> NoMore
- remaining -> Last(list.reverse(remaining))
- }
- 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)
- }
+ 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)
}
}
}
+}
- 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))
- }
+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
- /// as a partial chunk, with less than `count` elements.
- ///
- /// For any `count` less than 1 this function behaves as if it was set to 1.
- ///
- /// ## Examples
- ///
- /// > from_list([1, 2, 3, 4, 5, 6]) |> chunk(into: 2) |> to_list
- /// [[1, 2], [3, 4], [5, 6]]
- ///
- /// > from_list([1, 2, 3, 4, 5, 6, 7, 8]) |> chunk(into: 3) |> to_list
- /// [[1, 2, 3], [4, 5, 6], [7, 8]]
- ///
- pub fn sized_chunk(
- over iterator: Iterator(element),
- into count: Int,
- ) -> Iterator(List(element)) {
- iterator.continuation
- |> do_sized_chunk(count)
- |> Iterator
- }
+/// Creates an iterator that emits chunks of given size.
+///
+/// If the last chunk does not have `count` elements, it is yielded
+/// as a partial chunk, with less than `count` elements.
+///
+/// For any `count` less than 1 this function behaves as if it was set to 1.
+///
+/// ## Examples
+///
+/// > from_list([1, 2, 3, 4, 5, 6]) |> chunk(into: 2) |> to_list
+/// [[1, 2], [3, 4], [5, 6]]
+///
+/// > from_list([1, 2, 3, 4, 5, 6, 7, 8]) |> chunk(into: 3) |> to_list
+/// [[1, 2, 3], [4, 5, 6], [7, 8]]
+///
+pub fn sized_chunk(
+ over iterator: Iterator(element),
+ into count: Int,
+) -> Iterator(List(element)) {
+ iterator.continuation
+ |> do_sized_chunk(count)
+ |> Iterator
+}
- fn do_intersperse(
- continuation: fn() -> Action(element),
- separator: element,
- ) -> Action(element) {
- case continuation() {
- Stop -> Stop
- Continue(e, next) -> {
- let next_interspersed = fn() { do_intersperse(next, separator) }
- Continue(separator, fn() { Continue(e, next_interspersed) })
- }
+fn do_intersperse(
+ continuation: fn() -> Action(element),
+ separator: element,
+) -> Action(element) {
+ case continuation() {
+ Stop -> Stop
+ Continue(e, next) -> {
+ let next_interspersed = fn() { do_intersperse(next, separator) }
+ Continue(separator, fn() { Continue(e, next_interspersed) })
}
}
+}
- /// Creates an iterator that yields the given element
- /// between elements emitted by the underlying iterator.
- ///
- /// ## Examples
- ///
- /// > empty() |> intersperse(with: 0) |> to_list
- /// []
- ///
- /// > from_list([1]) |> intersperse(with: 0) |> to_list
- /// [1]
- ///
- /// > from_list([1, 2, 3, 4, 5]) |> intersperse(with: 0) |> to_list
- /// [1, 0, 2, 0, 3, 0, 4, 0, 5]
- ///
- pub fn intersperse(
- over iterator: Iterator(element),
- with elem: element,
- ) -> Iterator(element) {
- fn() {
- case iterator.continuation() {
- Stop -> Stop
- Continue(e, next) -> Continue(e, fn() { do_intersperse(next, elem) })
- }
+/// Creates an iterator that yields the given element
+/// between elements emitted by the underlying iterator.
+///
+/// ## Examples
+///
+/// > empty() |> intersperse(with: 0) |> to_list
+/// []
+///
+/// > from_list([1]) |> intersperse(with: 0) |> to_list
+/// [1]
+///
+/// > from_list([1, 2, 3, 4, 5]) |> intersperse(with: 0) |> to_list
+/// [1, 0, 2, 0, 3, 0, 4, 0, 5]
+///
+pub fn intersperse(
+ over iterator: Iterator(element),
+ with elem: element,
+) -> Iterator(element) {
+ fn() {
+ case iterator.continuation() {
+ Stop -> Stop
+ Continue(e, next) -> Continue(e, fn() { do_intersperse(next, elem) })
}
- |> Iterator
}
+ |> Iterator
+}
- fn do_any(
- continuation: fn() -> Action(element),
- predicate: fn(element) -> Bool,
- ) -> Bool {
- case continuation() {
- Stop -> False
- Continue(e, next) -> predicate(e) || do_any(next, predicate)
- }
+fn do_any(
+ continuation: fn() -> Action(element),
+ predicate: fn(element) -> Bool,
+) -> Bool {
+ case continuation() {
+ Stop -> False
+ Continue(e, next) -> predicate(e) || do_any(next, predicate)
}
+}
- /// Returns `True` if any element emitted by the iterator satisfies the given predicate,
- /// `False` otherwise.
- ///
- /// This function short-circuits once it finds a satisfying element.
- ///
- /// An empty iterator results in `False`.
- ///
- /// ## Examples
- ///
- /// > empty() |> any(fn(n) { n % 2 == 0 })
- /// False
- ///
- /// > from_list([1, 2, 5, 7, 9]) |> any(fn(n) { n % 2 == 0 })
- /// True
- ///
- /// > from_list([1, 3, 5, 7, 9]) |> any(fn(n) { n % 2 == 0 })
- /// False
- ///
- pub fn any(
- in iterator: Iterator(element),
- satisfying predicate: fn(element) -> Bool,
- ) -> Bool {
- iterator.continuation
- |> do_any(predicate)
- }
+/// Returns `True` if any element emitted by the iterator satisfies the given predicate,
+/// `False` otherwise.
+///
+/// This function short-circuits once it finds a satisfying element.
+///
+/// An empty iterator results in `False`.
+///
+/// ## Examples
+///
+/// > empty() |> any(fn(n) { n % 2 == 0 })
+/// False
+///
+/// > from_list([1, 2, 5, 7, 9]) |> any(fn(n) { n % 2 == 0 })
+/// True
+///
+/// > from_list([1, 3, 5, 7, 9]) |> any(fn(n) { n % 2 == 0 })
+/// False
+///
+pub fn any(
+ in iterator: Iterator(element),
+ satisfying predicate: fn(element) -> Bool,
+) -> Bool {
+ iterator.continuation
+ |> do_any(predicate)
+}
- fn do_all(
- continuation: fn() -> Action(element),
- predicate: fn(element) -> Bool,
- ) -> Bool {
- case continuation() {
- Stop -> True
- Continue(e, next) -> predicate(e) && do_all(next, predicate)
- }
+fn do_all(
+ continuation: fn() -> Action(element),
+ predicate: fn(element) -> Bool,
+) -> Bool {
+ case continuation() {
+ Stop -> True
+ Continue(e, next) -> predicate(e) && do_all(next, predicate)
}
+}
- /// Returns `True` if all elements emitted by the iterator satisfy the given predicate,
- /// `False` otherwise.
- ///
- /// This function short-circuits once it finds a non-satisfying element.
- ///
- /// An empty iterator results in `True`.
- ///
- /// ## Examples
- ///
- /// > empty() |> all(fn(n) { n % 2 == 0 })
- /// True
- ///
- /// > from_list([2, 4, 6, 8]) |> all(fn(n) { n % 2 == 0 })
- /// True
- ///
- /// > from_list([2, 4, 5, 8]) |> all(fn(n) { n % 2 == 0 })
- /// False
- ///
- pub fn all(
- in iterator: Iterator(element),
- satisfying predicate: fn(element) -> Bool,
- ) -> Bool {
- iterator.continuation
- |> do_all(predicate)
- }
+/// Returns `True` if all elements emitted by the iterator satisfy the given predicate,
+/// `False` otherwise.
+///
+/// This function short-circuits once it finds a non-satisfying element.
+///
+/// An empty iterator results in `True`.
+///
+/// ## Examples
+///
+/// > empty() |> all(fn(n) { n % 2 == 0 })
+/// True
+///
+/// > from_list([2, 4, 6, 8]) |> all(fn(n) { n % 2 == 0 })
+/// True
+///
+/// > from_list([2, 4, 5, 8]) |> all(fn(n) { n % 2 == 0 })
+/// False
+///
+pub fn all(
+ in iterator: Iterator(element),
+ satisfying predicate: fn(element) -> Bool,
+) -> Bool {
+ iterator.continuation
+ |> do_all(predicate)
+}
+if erlang {
fn update_group_with(
el: element,
) -> fn(Option(List(element))) -> List(element) {
diff --git a/src/gleam/string.gleam b/src/gleam/string.gleam
index 8379061..33f2ca7 100644
--- a/src/gleam/string.gleam
+++ b/src/gleam/string.gleam
@@ -182,134 +182,183 @@ if javascript {
"../gleam_stdlib.js" "less_than"
}
-if erlang {
- external fn erl_slice(String, Int, Int) -> String =
- "string" "slice"
-
- /// Takes a substring given a start and end Grapheme indexes. Negative indexes
- /// are taken starting from the *end* of the list.
- ///
- /// ## Examples
- /// > slice(from: "gleam", at_index: 1, length: 2)
- /// "le"
- ///
- /// > slice(from: "gleam", at_index: 1, length: 10)
- /// "leam"
- ///
- /// > slice(from: "gleam", at_index: 10, length: 3)
- /// ""
- ///
- /// > slice(from: "gleam", at_index: -2, length: 2)
- /// "am"
- ///
- /// > slice(from: "gleam", at_index: -12, length: 2)
- /// ""
- ///
- pub fn slice(
- from string: String,
- at_index idx: Int,
- length len: Int,
- ) -> String {
- case len < 0 {
- True -> ""
- False ->
- case idx < 0 {
- True -> {
- let translated_idx = length(string) + idx
- case translated_idx < 0 {
- True -> ""
- False -> erl_slice(string, translated_idx, len)
- }
+/// Takes a substring given a start and end Grapheme indexes. Negative indexes
+/// are taken starting from the *end* of the list.
+///
+/// ## Examples
+/// > slice(from: "gleam", at_index: 1, length: 2)
+/// "le"
+///
+/// > slice(from: "gleam", at_index: 1, length: 10)
+/// "leam"
+///
+/// > slice(from: "gleam", at_index: 10, length: 3)
+/// ""
+///
+/// > slice(from: "gleam", at_index: -2, length: 2)
+/// "am"
+///
+/// > slice(from: "gleam", at_index: -12, length: 2)
+/// ""
+///
+pub fn slice(from string: String, at_index idx: Int, length len: Int) -> String {
+ case len < 0 {
+ True -> ""
+ False ->
+ case idx < 0 {
+ True -> {
+ let translated_idx = length(string) + idx
+ case translated_idx < 0 {
+ True -> ""
+ False -> do_slice(string, translated_idx, len)
}
- False -> erl_slice(string, idx, len)
}
- }
+ False -> do_slice(string, idx, len)
+ }
}
+}
- /// Drops contents of the first string that occur before the second string.
- /// If the first string does not contain the second string, the first string is returned.
- ///
- /// ## Examples
- /// > crop(from: "The Lone Gunmen", before: "Lone")
- /// "Lone Gunmen"
- ///
- pub fn crop(from string: String, before substring: String) -> String {
+if erlang {
+ external fn do_slice(String, Int, Int) -> String =
+ "string" "slice"
+}
+
+if javascript {
+ external fn do_slice(String, Int, Int) -> String =
+ "../gleam_stdlib.js" "slice_string"
+}
+
+/// Drops contents of the first string that occur before the second string.
+/// If the first string does not contain the second string, the first string is returned.
+///
+/// ## Examples
+/// > crop(from: "The Lone Gunmen", before: "Lone")
+/// "Lone Gunmen"
+///
+pub fn crop(from string: String, before substring: String) -> String {
+ do_crop(string, substring)
+}
+
+if erlang {
+ fn do_crop(string: String, substring: String) -> String {
string
|> erl_contains(substring)
|> dynamic.string()
|> result.unwrap(string)
}
- /// Drops *n* Graphemes from the left side of a string.
- ///
- /// ## Examples
- /// > drop_left(from: "The Lone Gunmen", up_to: 2)
- /// "e Lone Gunmen"
- ///
- pub fn drop_left(from string: String, up_to num_graphemes: Int) -> String {
- case num_graphemes < 0 {
- True -> string
- False -> slice(string, num_graphemes, length(string) - num_graphemes)
- }
+ external fn erl_contains(String, String) -> Dynamic =
+ "string" "find"
+}
+
+if javascript {
+ external fn do_crop(String, String) -> String =
+ "../gleam_stdlib.js" "crop_string"
+}
+
+/// Drops *n* Graphemes from the left side of a string.
+///
+/// ## Examples
+/// > drop_left(from: "The Lone Gunmen", up_to: 2)
+/// "e Lone Gunmen"
+///
+pub fn drop_left(from string: String, up_to num_graphemes: Int) -> String {
+ case num_graphemes < 0 {
+ True -> string
+ False -> slice(string, num_graphemes, length(string) - num_graphemes)
}
+}
- /// Drops *n* Graphemes from the right side of a string.
- ///
- /// ## Examples
- /// > drop_right(from: "Cigarette Smoking Man", up_to: 2)
- /// "Cigarette Smoking M"
- ///
- pub fn drop_right(from string: String, up_to num_graphemes: Int) -> String {
- case num_graphemes < 0 {
- True -> string
- False -> slice(string, 0, length(string) - num_graphemes)
- }
+/// Drops *n* Graphemes from the right side of a string.
+///
+/// ## Examples
+/// > drop_right(from: "Cigarette Smoking Man", up_to: 2)
+/// "Cigarette Smoking M"
+///
+pub fn drop_right(from string: String, up_to num_graphemes: Int) -> String {
+ case num_graphemes < 0 {
+ True -> string
+ False -> slice(string, 0, length(string) - num_graphemes)
}
+}
- external fn erl_contains(String, String) -> Dynamic =
- "string" "find"
+/// Checks if the first string contains the second.
+///
+/// ## Examples
+///
+/// > contains(does: "theory", contain: "ory")
+/// True
+///
+/// > contains(does: "theory", contain: "the")
+/// True
+///
+/// > contains(does: "theory", contain: "THE")
+/// False
+///
+pub fn contains(does haystack: String, contain needle: String) -> Bool {
+ do_contains(haystack, needle)
+}
- /// Checks if the first string contains the second.
- ///
- /// ## Examples
- ///
- /// > contains(does: "theory", contain: "ory")
- /// True
- ///
- /// > contains(does: "theory", contain: "the")
- /// True
- ///
- /// > contains(does: "theory", contain: "THE")
- /// False
- ///
- pub fn contains(does haystack: String, contain needle: String) -> Bool {
+if erlang {
+ fn do_contains(haystack: String, needle: String) -> Bool {
haystack
|> erl_contains(needle)
|> dynamic.atom
|> result.is_error
}
+}
- /// Checks whether the first string starts with the second one.
- ///
- /// ## Examples
- ///
- /// > starts_with("theory", "ory")
- /// False
- ///
- pub external fn starts_with(String, String) -> Bool =
+if javascript {
+ fn do_contains(haystack: String, needle: String) -> Bool {
+ index_of(haystack, needle) != -1
+ }
+
+ external fn index_of(String, String) -> Int =
+ "../gleam_stdlib.js" "index_of"
+}
+
+/// Checks whether the first string starts with the second one.
+///
+/// ## Examples
+///
+/// > starts_with("theory", "ory")
+/// False
+///
+pub fn starts_with(string: String, prefix: String) -> Bool {
+ do_starts_with(string, prefix)
+}
+
+if erlang {
+ external fn do_starts_with(String, String) -> Bool =
"gleam_stdlib" "string_starts_with"
+}
- /// Checks whether the first string ends with the second one.
- ///
- /// ## Examples
- ///
- /// > ends_with("theory", "ory")
- /// True
- ///
- pub external fn ends_with(String, String) -> Bool =
+if javascript {
+ external fn do_starts_with(String, String) -> Bool =
+ "../gleam_stdlib.js" "starts_with"
+}
+
+/// Checks whether the first string ends with the second one.
+///
+/// ## Examples
+///
+/// > ends_with("theory", "ory")
+/// True
+///
+pub fn ends_with(string: String, suffix: String) -> Bool {
+ do_ends_with(string, suffix)
+}
+
+if erlang {
+ external fn do_ends_with(String, String) -> Bool =
"gleam_stdlib" "string_ends_with"
}
+if javascript {
+ external fn do_ends_with(String, String) -> Bool =
+ "../gleam_stdlib.js" "ends_with"
+}
+
/// Creates a list of strings by splitting a given string on a given substring.
///
/// ## Examples
@@ -324,25 +373,32 @@ pub fn split(x: String, on substring: String) -> List(String) {
|> list.map(with: string_builder.to_string)
}
+/// Splits a string a single time on the given substring.
+///
+/// Returns an error if substring not present.
+///
+/// ## Examples
+///
+/// > split_once("home/gleam/desktop/", on: "/")
+/// Ok(#("home", "gleam/desktop/"))
+///
+/// > split_once("home/gleam/desktop/", on: "?")
+/// Error(Nil)
+///
+pub fn split_once(
+ x: String,
+ on substring: String,
+) -> Result(#(String, String), Nil) {
+ do_split_once(x, substring)
+}
+
if erlang {
external fn erl_split(String, String) -> List(String) =
"string" "split"
- /// Splits a string a single time on the given substring.
- ///
- /// Returns an error if substring not present.
- ///
- /// ## Examples
- ///
- /// > split_once("home/gleam/desktop/", on: "/")
- /// Ok(#("home", "gleam/desktop/"))
- ///
- /// > split_once("home/gleam/desktop/", on: "?")
- /// Error(Nil)
- ///
- pub fn split_once(
+ fn do_split_once(
x: String,
- on substring: String,
+ substring: String,
) -> Result(#(String, String), Nil) {
case erl_split(x, substring) {
[first, rest] -> Ok(#(first, rest))
@@ -351,6 +407,14 @@ if erlang {
}
}
+if javascript {
+ external fn do_split_once(
+ x: String,
+ substring: String,
+ ) -> Result(#(String, String), Nil) =
+ "../gleam_stdlib.js" "split_once"
+}
+
/// Creates a new string by joining two strings together.
///
/// This function copies both strings and runs in linear time. If you find
@@ -386,38 +450,38 @@ pub fn concat(strings: List(String)) -> String {
|> string_builder.to_string
}
-if erlang {
- /// Creates a new string by repeating a string a given number of times.
- ///
- /// This function runs in linear time.
- ///
- /// ## Examples
- ///
- /// > repeat("ha", times: 3)
- /// "hahaha"
- ///
- pub fn repeat(string: String, times times: Int) -> String {
- iterator.repeat(string)
- |> iterator.take(times)
- |> iterator.to_list
- |> concat
- }
+/// Creates a new string by repeating a string a given number of times.
+///
+/// This function runs in linear time.
+///
+/// ## Examples
+///
+/// > repeat("ha", times: 3)
+/// "hahaha"
+///
+pub fn repeat(string: String, times times: Int) -> String {
+ iterator.repeat(string)
+ |> iterator.take(times)
+ |> iterator.to_list
+ |> concat
+}
- /// Joins many strings together with a given separator.
- ///
- /// This function runs in linear time.
- ///
- /// ## Examples
- ///
- /// > join(["home","evan","Desktop"], with: "/")
- /// "home/evan/Desktop"
- ///
- pub fn join(strings: List(String), with separator: String) -> String {
- strings
- |> list.intersperse(with: separator)
- |> concat
- }
+/// Joins many strings together with a given separator.
+///
+/// This function runs in linear time.
+///
+/// ## Examples
+///
+/// > join(["home","evan","Desktop"], with: "/")
+/// "home/evan/Desktop"
+///
+pub fn join(strings: List(String), with separator: String) -> String {
+ strings
+ |> list.intersperse(with: separator)
+ |> concat
+}
+if erlang {
type Direction {
Leading
Trailing
@@ -460,66 +524,110 @@ if erlang {
pub fn pad_right(string: String, to length: Int, with pad_string: String) {
erl_pad(string, length, Trailing, pad_string)
}
+}
- external fn erl_trim(String, Direction) -> String =
- "string" "trim"
+/// Removes whitespace on both sides of a String.
+///
+/// ## Examples
+///
+/// > trim(" hats \n")
+/// "hats"
+///
+pub fn trim(string: String) -> String {
+ do_trim(string)
+}
- /// Removes whitespace on both sides of a String.
- ///
- /// ## Examples
- ///
- /// > trim(" hats \n")
- /// "hats"
- ///
- pub fn trim(string: String) -> String {
+if erlang {
+ fn do_trim(string: String) -> String {
erl_trim(string, Both)
}
- /// Removes whitespace on the left of a String.
- ///
- /// ## Examples
- ///
- /// > trim_left(" hats \n")
- /// "hats \n"
- ///
- pub fn trim_left(string: String) -> String {
+ external fn erl_trim(String, Direction) -> String =
+ "string" "trim"
+}
+
+if javascript {
+ external fn do_trim(string: String) -> String =
+ "../gleam_stdlib.js" "trim"
+}
+
+/// Removes whitespace on the left of a String.
+///
+/// ## Examples
+///
+/// > trim_left(" hats \n")
+/// "hats \n"
+///
+pub fn trim_left(string: String) -> String {
+ do_trim_left(string)
+}
+
+if erlang {
+ fn do_trim_left(string: String) -> String {
erl_trim(string, Leading)
}
+}
- /// Removes whitespace on the right of a String.
- ///
- /// ## Examples
- ///
- /// > trim_right(" hats \n")
- /// " hats"
- ///
- pub fn trim_right(string: String) -> String {
+if javascript {
+ external fn do_trim_left(string: String) -> String =
+ "../gleam_stdlib.js" "trim_left"
+}
+
+/// Removes whitespace on the right of a String.
+///
+/// ## Examples
+///
+/// > trim_right(" hats \n")
+/// " hats"
+///
+pub fn trim_right(string: String) -> String {
+ do_trim_right(string)
+}
+
+if erlang {
+ fn do_trim_right(string: String) -> String {
erl_trim(string, Trailing)
}
+}
- /// Splits a non-empty string into its head and tail. This lets you
- /// pattern match on strings exactly as you would with lists.
- ///
- /// ## Examples
- /// > pop_grapheme("gleam")
- /// Ok(#("g", "leam"))
- ///
- /// > pop_grapheme("")
- /// Error(Nil)
- ///
- pub external fn pop_grapheme(string: String) -> Result(#(String, String), Nil) =
+if javascript {
+ external fn do_trim_right(string: String) -> String =
+ "../gleam_stdlib.js" "trim_right"
+}
+
+/// Splits a non-empty string into its head and tail. This lets you
+/// pattern match on strings exactly as you would with lists.
+///
+/// ## Examples
+/// > pop_grapheme("gleam")
+/// Ok(#("g", "leam"))
+///
+/// > pop_grapheme("")
+/// Error(Nil)
+///
+pub fn pop_grapheme(string: String) -> Result(#(String, String), Nil) {
+ do_pop_grapheme(string)
+}
+
+if erlang {
+ external fn do_pop_grapheme(string: String) -> Result(#(String, String), Nil) =
"gleam_stdlib" "string_pop_grapheme"
+}
- /// Converts a string to a list of Graphemes.
- ///
- /// > to_graphemes("abc")
- /// ["a", "b", "c"]
- ///
- pub fn to_graphemes(string: String) -> List(String) {
- case pop_grapheme(string) {
- Ok(#(grapheme, rest)) -> [grapheme, ..to_graphemes(rest)]
- _ -> []
- }
+if javascript {
+ external fn do_pop_grapheme(string: String) -> Result(#(String, String), Nil) =
+ "../gleam_stdlib.js" "pop_grapheme"
+}
+
+/// Converts a string to a list of Graphemes.
+///
+/// > to_graphemes("abc")
+/// ["a", "b", "c"]
+///
+pub fn to_graphemes(string: String) -> List(String) {
+ case pop_grapheme(string) {
+ Ok(#(grapheme, rest)) -> [grapheme, ..to_graphemes(rest)]
+ _ -> []
}
}
diff --git a/src/gleam/string_builder.gleam b/src/gleam/string_builder.gleam
index f39ce49..ac6817f 100644
--- a/src/gleam/string_builder.gleam
+++ b/src/gleam/string_builder.gleam
@@ -127,7 +127,7 @@ pub fn to_string(builder: StringBuilder) -> String {
if erlang {
external fn do_to_string(StringBuilder) -> String =
- "erlang" "iolist_to_binary"
+ "unicode" "characters_to_binary"
}
if javascript {
@@ -232,7 +232,7 @@ if erlang {
"string" "split"
fn do_split(iodata: StringBuilder, pattern: String) -> List(StringBuilder) {
- do_split(iodata, pattern, All)
+ erl_split(iodata, pattern, All)
}
}
diff --git a/src/gleam_stdlib.erl b/src/gleam_stdlib.erl
index 836bdbe..49a3b0c 100644
--- a/src/gleam_stdlib.erl
+++ b/src/gleam_stdlib.erl
@@ -12,7 +12,7 @@
bit_string_int_to_u32/1, bit_string_int_from_u32/1,
bit_string_append/2, bit_string_part_/3, decode_bit_string/1,
compile_regex/2, regex_match/2, regex_split/2, regex_scan/2,
- base_decode64/1, wrap_list/1, rescue/1, get_line/1]).
+ base_decode64/1, wrap_list/1, get_line/1]).
should_equal(Actual, Expected) ->
?assertEqual(Expected, Actual),
diff --git a/src/gleam_stdlib.js b/src/gleam_stdlib.js
index a45aa89..89908de 100644
--- a/src/gleam_stdlib.js
+++ b/src/gleam_stdlib.js
@@ -1,3 +1,5 @@
+const Nil = undefined;
+
function to_list(array) {
let list = [];
for (let item of array.reverse()) {
@@ -6,15 +8,23 @@ function to_list(array) {
return list;
}
+function Ok(x) {
+ return { type: "Ok", 0: x };
+}
+
+function Error(x) {
+ return { type: "Error", 0: x };
+}
+
export function identity(x) {
return x;
}
export function parse_int(value) {
if (/^[-+]?(\d+)$/.test(value)) {
- return { type: "Ok", 0: Number(value) };
+ return Ok(Number(value));
} else {
- return { type: "Error", 0: null };
+ return Error(Nil);
}
}
@@ -35,9 +45,10 @@ export function string_reverse(string) {
}
export function string_length(string) {
- if (Intl && Intl.Segmenter) {
+ let iterator = graphemes_iterator(string);
+ if (iterator) {
let i = 0;
- for (let _ of new Intl.Segmenter("en-gb").segment(string)) {
+ for (let _ of iterator) {
i++;
}
return i;
@@ -46,6 +57,27 @@ export function string_length(string) {
}
}
+function graphemes_iterator(string) {
+ if (Intl && Intl.Segmenter) {
+ return new Intl.Segmenter("en-gb").segment(string)[Symbol.iterator]();
+ }
+}
+
+export function pop_grapheme(string) {
+ let first;
+ let iterator = graphemes_iterator(string);
+ if (iterator) {
+ first = iterator.next().value?.segment;
+ } else {
+ first = string.match(/./u)?.[0];
+ }
+ if (first) {
+ return Ok([first, string.slice(first.length)]);
+ } else {
+ return Error(Nil);
+ }
+}
+
export function lowercase(string) {
return string.toLowerCase();
}
@@ -71,7 +103,7 @@ export function split(xs, pattern) {
}
export function join(xs) {
- return xs.flat().join("");
+ return xs.flat(Infinity).join("");
}
export function byte_size(data) {
@@ -83,3 +115,46 @@ export function byte_size(data) {
return data.length;
}
}
+
+export function slice_string(string, from, length) {
+ return string.slice(from, from + length);
+}
+
+export function crop_string(string, substring) {
+ return string.substring(string.indexOf(substring));
+}
+
+export function index_of(haystack, needle) {
+ return haystack.indexOf(needle) | 0;
+}
+
+export function starts_with(haystack, needle) {
+ return haystack.startsWith(needle);
+}
+
+export function ends_with(haystack, needle) {
+ return haystack.endsWith(needle);
+}
+
+export function split_once(haystack, needle) {
+ let index = haystack.indexOf(needle);
+ if (index >= 0) {
+ let before = haystack.slice(0, index);
+ let after = haystack.slice(index + needle.length);
+ return Ok([before, after]);
+ } else {
+ return Error(Nil);
+ }
+}
+
+export function trim(string) {
+ return string.trim();
+}
+
+export function trim_left(string) {
+ return string.trimLeft();
+}
+
+export function trim_right(string) {
+ return string.trimRight();
+}
diff --git a/test/gleam/string_test.gleam b/test/gleam/string_test.gleam
index a890d0f..b6762ca 100644
--- a/test/gleam/string_test.gleam
+++ b/test/gleam/string_test.gleam
@@ -43,20 +43,18 @@ pub fn split_test() {
|> should.equal(["Gleam", "Erlang,Elixir"])
}
-if erlang {
- pub fn split_once_test() {
- "Gleam,Erlang,Elixir"
- |> string.split_once(",")
- |> should.equal(Ok(#("Gleam", "Erlang,Elixir")))
-
- "Gleam"
- |> string.split_once(",")
- |> should.equal(Error(Nil))
-
- ""
- |> string.split_once(",")
- |> should.equal(Error(Nil))
- }
+pub fn split_once_test() {
+ "Gleam,Erlang,Elixir"
+ |> string.split_once(",")
+ |> should.equal(Ok(#("Gleam", "Erlang,Elixir")))
+
+ "Gleam"
+ |> string.split_once(",")
+ |> should.equal(Error(Nil))
+
+ ""
+ |> string.split_once(",")
+ |> should.equal(Error(Nil))
}
pub fn replace_test() {
@@ -65,12 +63,10 @@ pub fn replace_test() {
|> should.equal("Gleam++Erlang++Elixir")
}
-if erlang {
- pub fn append_test() {
- "Test"
- |> string.append(" Me")
- |> should.equal("Test Me")
- }
+pub fn append_test() {
+ "Test"
+ |> string.append(" Me")
+ |> should.equal("Test Me")
}
pub fn compare_test() {
@@ -90,179 +86,179 @@ pub fn compare_test() {
|> should.equal(order.Gt)
}
-if erlang {
- pub fn contains_test() {
- "gleam"
- |> string.contains("ea")
- |> should.equal(True)
+pub fn contains_test() {
+ "gleam"
+ |> string.contains("ea")
+ |> should.equal(True)
- "gleam"
- |> string.contains("x")
- |> should.equal(False)
+ "gleam"
+ |> string.contains("x")
+ |> should.equal(False)
- string.contains(does: "bellwether", contain: "bell")
- |> should.equal(True)
- }
+ string.contains(does: "bellwether", contain: "bell")
+ |> should.equal(True)
+}
- pub fn concat_test() {
- ["Hello", ", ", "world!"]
- |> string.concat
- |> should.equal("Hello, world!")
- }
+pub fn concat_test() {
+ ["Hello", ", ", "world!"]
+ |> string.concat
+ |> should.equal("Hello, world!")
+}
- pub fn repeat_test() {
- "hi"
- |> string.repeat(times: 3)
- |> should.equal("hihihi")
+pub fn repeat_test() {
+ "hi"
+ |> string.repeat(times: 3)
+ |> should.equal("hihihi")
- "hi"
- |> string.repeat(0)
- |> should.equal("")
+ "hi"
+ |> string.repeat(0)
+ |> should.equal("")
- "hi"
- |> string.repeat(-1)
- |> should.equal("")
- }
+ "hi"
+ |> string.repeat(-1)
+ |> should.equal("")
+}
- pub fn join_test() {
- ["Hello", "world!"]
- |> string.join(with: ", ")
- |> should.equal("Hello, world!")
+pub fn join_test() {
+ ["Hello", "world!"]
+ |> string.join(with: ", ")
+ |> should.equal("Hello, world!")
- ["Hello", "world!"]
- |> string.join(with: "-")
- |> should.equal("Hello-world!")
- }
+ ["Hello", "world!"]
+ |> string.join(with: "-")
+ |> should.equal("Hello-world!")
+}
- pub fn trim_test() {
- " hats \n"
- |> string.trim()
- |> should.equal("hats")
- }
+pub fn trim_test() {
+ " hats \n"
+ |> string.trim()
+ |> should.equal("hats")
+}
- pub fn trim_left_test() {
- " hats \n"
- |> string.trim_left()
- |> should.equal("hats \n")
- }
+pub fn trim_left_test() {
+ " hats \n"
+ |> string.trim_left()
+ |> should.equal("hats \n")
+}
- pub fn trim_right_test() {
- " hats \n"
- |> string.trim_right()
- |> should.equal(" hats")
- }
+pub fn trim_right_test() {
+ " hats \n"
+ |> string.trim_right()
+ |> should.equal(" hats")
+}
- pub fn starts_with_test() {
- "theory"
- |> string.starts_with("")
- |> should.equal(True)
+pub fn starts_with_test() {
+ "theory"
+ |> string.starts_with("")
+ |> should.equal(True)
- "theory"
- |> string.starts_with("the")
- |> should.equal(True)
+ "theory"
+ |> string.starts_with("the")
+ |> should.equal(True)
- "theory"
- |> string.starts_with("ory")
- |> should.equal(False)
+ "theory"
+ |> string.starts_with("ory")
+ |> should.equal(False)
- "theory"
- |> string.starts_with("theory2")
- |> should.equal(False)
- }
+ "theory"
+ |> string.starts_with("theory2")
+ |> should.equal(False)
+}
- pub fn ends_with_test() {
- "theory"
- |> string.ends_with("")
- |> should.equal(True)
+pub fn ends_with_test() {
+ "theory"
+ |> string.ends_with("")
+ |> should.equal(True)
- "theory"
- |> string.ends_with("ory")
- |> should.equal(True)
+ "theory"
+ |> string.ends_with("ory")
+ |> should.equal(True)
- "theory"
- |> string.ends_with("the")
- |> should.equal(False)
+ "theory"
+ |> string.ends_with("the")
+ |> should.equal(False)
- "theory"
- |> string.ends_with("theory2")
- |> should.equal(False)
- }
+ "theory"
+ |> string.ends_with("theory2")
+ |> should.equal(False)
+}
- pub fn slice_test() {
- "gleam"
- |> string.slice(at_index: 1, length: 2)
- |> should.equal("le")
+pub fn slice_test() {
+ "gleam"
+ |> string.slice(at_index: 1, length: 2)
+ |> should.equal("le")
- "gleam"
- |> string.slice(at_index: 1, length: 10)
- |> should.equal("leam")
+ "gleam"
+ |> string.slice(at_index: 1, length: 10)
+ |> should.equal("leam")
- "gleam"
- |> string.slice(at_index: 10, length: 3)
- |> should.equal("")
+ "gleam"
+ |> string.slice(at_index: 10, length: 3)
+ |> should.equal("")
- "gleam"
- |> string.slice(at_index: -2, length: 2)
- |> should.equal("am")
+ "gleam"
+ |> string.slice(at_index: -2, length: 2)
+ |> should.equal("am")
- "gleam"
- |> string.slice(at_index: -12, length: 2)
- |> should.equal("")
+ "gleam"
+ |> string.slice(at_index: -12, length: 2)
+ |> should.equal("")
- "gleam"
- |> string.slice(at_index: 2, length: -3)
- |> should.equal("")
- }
+ "gleam"
+ |> string.slice(at_index: 2, length: -3)
+ |> should.equal("")
+}
- pub fn crop_test() {
- "gleam"
- |> string.crop("gl")
- |> should.equal("gleam")
+pub fn crop_test() {
+ "gleam"
+ |> string.crop("gl")
+ |> should.equal("gleam")
- "gleam"
- |> string.crop("le")
- |> should.equal("leam")
+ "gleam"
+ |> string.crop("le")
+ |> should.equal("leam")
- string.crop(from: "gleam", before: "ea")
- |> should.equal("eam")
+ string.crop(from: "gleam", before: "ea")
+ |> should.equal("eam")
- "gleam"
- |> string.crop("")
- |> should.equal("gleam")
+ "gleam"
+ |> string.crop("")
+ |> should.equal("gleam")
- "gleam"
- |> string.crop("!")
- |> should.equal("gleam")
- }
+ "gleam"
+ |> string.crop("!")
+ |> should.equal("gleam")
+}
- pub fn drop_left_test() {
- "gleam"
- |> string.drop_left(up_to: 2)
- |> should.equal("eam")
+pub fn drop_left_test() {
+ "gleam"
+ |> string.drop_left(up_to: 2)
+ |> should.equal("eam")
- "gleam"
- |> string.drop_left(up_to: 6)
- |> should.equal("")
+ "gleam"
+ |> string.drop_left(up_to: 6)
+ |> should.equal("")
- "gleam"
- |> string.drop_left(up_to: -2)
- |> should.equal("gleam")
- }
+ "gleam"
+ |> string.drop_left(up_to: -2)
+ |> should.equal("gleam")
+}
- pub fn drop_right_test() {
- "gleam"
- |> string.drop_right(up_to: 2)
- |> should.equal("gle")
+pub fn drop_right_test() {
+ "gleam"
+ |> string.drop_right(up_to: 2)
+ |> should.equal("gle")
- "gleam"
- |> string.drop_right(up_to: 5)
- |> should.equal("")
+ "gleam"
+ |> string.drop_right(up_to: 5)
+ |> should.equal("")
- "gleam"
- |> string.drop_right(up_to: -2)
- |> should.equal("gleam")
- }
+ "gleam"
+ |> string.drop_right(up_to: -2)
+ |> should.equal("gleam")
+}
+if erlang {
pub fn pad_left_test() {
"121"
|> string.pad_left(to: 5, with: ".")
@@ -298,35 +294,37 @@ if erlang {
|> string.pad_right(to: 5, with: "XY")
|> should.equal("121XYXY")
}
+}
- pub fn pop_grapheme_test() {
- "gleam"
- |> string.pop_grapheme()
- |> should.equal(Ok(#("g", "leam")))
+pub fn pop_grapheme_test() {
+ "gleam"
+ |> string.pop_grapheme()
+ |> should.equal(Ok(#("g", "leam")))
- "g"
- |> string.pop_grapheme()
- |> should.equal(Ok(#("g", "")))
+ "g"
+ |> string.pop_grapheme()
+ |> should.equal(Ok(#("g", "")))
- ""
- |> string.pop_grapheme()
- |> should.equal(Error(Nil))
- }
+ ""
+ |> string.pop_grapheme()
+ |> should.equal(Error(Nil))
+}
- pub fn to_graphemes_test() {
- "abc"
- |> string.to_graphemes()
- |> should.equal(["a", "b", "c"])
+pub fn to_graphemes_test() {
+ "abc"
+ |> string.to_graphemes()
+ |> should.equal(["a", "b", "c"])
- "a"
- |> string.to_graphemes()
- |> should.equal(["a"])
+ "a"
+ |> string.to_graphemes()
+ |> should.equal(["a"])
- ""
- |> string.to_graphemes()
- |> should.equal([])
- }
+ ""
+ |> string.to_graphemes()
+ |> should.equal([])
+}
+if erlang {
pub fn utf_codepoint_test() {
string.utf_codepoint(1114444)
|> should.be_error