diff options
-rw-r--r-- | .github/workflows/test.yml | 21 | ||||
-rw-r--r-- | rebar.config | 11 | ||||
-rw-r--r-- | src/gleam/iterator.gleam | 140 | ||||
-rw-r--r-- | src/gleam_iterator.app.src | 14 | ||||
-rw-r--r-- | test/gleam/iterator_test.gleam | 0 |
5 files changed, 178 insertions, 8 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..6afde08 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,21 @@ +name: test + +on: + push: + branches: + - master + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1.0.0 + - uses: gleam-lang/setup-erlang@v1.0.0 + with: + otp-version: 22.1 + - uses: gleam-lang/setup-gleam@v1.0.1 + with: + gleam-version: 0.5.0-rc1 + - run: rebar3 install_deps + - run: rebar3 eunit diff --git a/rebar.config b/rebar.config index 87742d1..3f8f69d 100644 --- a/rebar.config +++ b/rebar.config @@ -2,14 +2,9 @@ {src_dirs, ["src", "gen/src"]}. {profiles, [ - {test, [ - {pre_hooks, [{compile, "gleam build ."}]}, - {src_dirs, ["src", "test", "gen/src", "gen/test"]} - ]}, - - {dev, [ - {pre_hooks, [{compile, "gleam build ."}]} - ]} + {test, [{src_dirs, ["src", "test", "gen/src", "gen/test"]}]} ]}. +{project_plugins, [rebar_gleam]}. + {deps, []}. diff --git a/src/gleam/iterator.gleam b/src/gleam/iterator.gleam new file mode 100644 index 0000000..53b80d3 --- /dev/null +++ b/src/gleam/iterator.gleam @@ -0,0 +1,140 @@ +import gleam/list + +// Internal private representation of an Iterator + +enum Action(element) { + // Improper dancing in the middle of the street + // Improper dancing in the middle of the street + // Improper dancing in the middle of the street + // Somebody better notify the chief of police + Stop + Continue(element, fn() -> Action(element)) + // Yes! +} + +// Wrapper to hide the internal representation + +pub external type Iterator(element); + +// TODO: remove once we have opaque type wrappers +external fn opaque(fn() -> Action(element)) -> Iterator(element) + = "gleam@iterator" "identity" + +external fn unopaque(Iterator(element)) -> fn() -> Action(element) + = "gleam@iterator" "identity" + +pub fn identity(x) { + x +} + +// Public API for iteration + +pub enum Step(element, acc) { + Next(element, acc) + Done +} + +// Creating Iterators + +fn do_unfold(initial, f) { + fn() { + case f(initial) { + Next(x, acc) -> Continue(x, do_unfold(acc, f)) + Done -> Stop + } + } +} + +// TODO: test +// TODO: document +pub fn unfold(from initial: acc, with f: fn(acc) -> Step(element, acc)) -> Iterator(element) { + opaque(do_unfold(initial, f)) +} + +// TODO: test +// TODO: document +pub fn repeatedly(f: fn() -> element) -> Iterator(element) { + unfold(Nil, fn(acc) { Next(f(), acc) }) +} + +// TODO: test +// TODO: document +pub fn repeat(x: element) -> Iterator(element) { + repeatedly(fn() { x }) +} + +// TODO: test +// TODO: document +pub fn from_list(list: List(element)) -> Iterator(element) { + unfold(list, fn(acc) { + case acc { + [] -> Done + [head | tail] -> Next(head, tail) + } + }) +} + +// Consuming Iterators + +fn do_fold(iterator, initial, f) { + case iterator() { + Continue(element, iterator) -> do_fold(iterator, f(element, initial), f) + Stop -> initial + } +} + +// TODO: test +// TODO: document +pub fn fold(over iterator: Iterator(e), from initial: acc, with f: fn(e, acc) -> acc) -> acc { + do_fold(unopaque(iterator), initial, f) +} + +// TODO: test +// TODO: document +// For side effects +pub fn run(iterator) -> Nil { + fold(iterator, Nil, fn(_, acc) { acc }) +} + +// TODO: test +// TODO: document +pub fn to_list(iterator: Iterator(element)) -> List(element) { + iterator + |> fold(_, [], fn(e, acc) { [e | acc] }) + |> list.reverse +} + +// Transforming Iterators + +fn do_map(iterator, f) { + fn() { + case iterator() { + Continue(e, iterator) -> Continue(f(e), do_map(iterator, f)) + Stop -> Stop + } + } +} + +// TODO: test +// TODO: document +pub fn map(over iterator: Iterator(a), with f: fn(a) -> b) -> Iterator(b) { + opaque(do_map(unopaque(iterator), f)) +} + +fn do_filter(iterator, predicate) { + fn() { + case iterator() { + Continue(e, iterator) -> case predicate(e) { + True -> Continue(e, iterator) + False -> do_filter(iterator, predicate)() + } + Stop -> Stop + } + } +} + +// TODO: test +// TODO: document +pub fn filter(iterator: Iterator(a), for predicate: fn(a) -> Bool) -> Iterator(a) { + opaque(do_filter(unopaque(iterator), predicate)) +} diff --git a/src/gleam_iterator.app.src b/src/gleam_iterator.app.src new file mode 100644 index 0000000..98defca --- /dev/null +++ b/src/gleam_iterator.app.src @@ -0,0 +1,14 @@ +{application, gleam_iterator, + [{description, "A Gleam program"}, + {vsn, "1.0.0"}, + {registered, []}, + {applications, + [kernel, + stdlib + ]}, + {env,[]}, + {modules, []}, + + {licenses, ["Apache 2.0"]}, + {links, []} +]}. diff --git a/test/gleam/iterator_test.gleam b/test/gleam/iterator_test.gleam new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/gleam/iterator_test.gleam |