···11+This is free and unencumbered software released into the public domain.
22+33+Anyone is free to copy, modify, publish, use, compile, sell, or
44+distribute this software, either in source code form or as a compiled
55+binary, for any purpose, commercial or non-commercial, and by any
66+means.
77+88+In jurisdictions that recognize copyright laws, the author or authors
99+of this software dedicate any and all copyright interest in the
1010+software to the public domain. We make this dedication for the benefit
1111+of the public at large and to the detriment of our heirs and
1212+successors. We intend this dedication to be an overt act of
1313+relinquishment in perpetuity of all present and future rights to this
1414+software under copyright law.
1515+1616+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1717+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1818+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
1919+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
2020+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2121+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2222+OTHER DEALINGS IN THE SOFTWARE.
2323+2424+For more information, please refer to <https://unlicense.org/>
+35
README.md
···11+# cycle
22+33+[](https://hex.pm/packages/cycle)
44+[](https://hexdocs.pm/cycle/)
55+66+```sh
77+gleam add cycle@1
88+```
99+```gleam
1010+import cycle
1111+1212+pub fn main() -> Nil {
1313+ echo cycle.start(with: #([], 0), run: fn(state) {
1414+ let #(is, i) = state
1515+ case i < 5 {
1616+ True -> {
1717+ let i = i + 1
1818+ cycle.continue(#(list.prepend(is, i), i))
1919+ }
2020+ _ -> cycle.stop(is)
2121+ }
2222+ })
2323+2424+ // output: [5, 4, 3, 2, 1]
2525+}
2626+```
2727+2828+Further documentation can be found at <https://hexdocs.pm/cycle>.
2929+3030+## Development
3131+3232+```sh
3333+gleam run # Run the cycle
3434+gleam test # Run the tests
3535+```
···11+name = "cycle"
22+version = "1.0.0"
33+44+description = "Put your function in a loop until the cycle (pun intended) breaks"
55+licences = ["Unlicense"]
66+repository = { type = "tangled", user = "fuzzko.neocities.org", repo = "cycle" }
77+88+[dev-dependencies]
99+gleeunit = ">= 1.9.0 and < 2.0.0"
+10
manifest.toml
···11+# This file was generated by Gleam
22+# You typically do not need to edit this file
33+44+packages = [
55+ { name = "gleam_stdlib", version = "0.69.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "AAB0962BEBFAA67A2FBEE9EEE218B057756808DC9AF77430F5182C6115B3A315" },
66+ { name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" },
77+]
88+99+[requirements]
1010+gleeunit = { version = ">= 1.9.0 and < 2.0.0" }
src/.gitkeep
This is a binary file and will not be displayed.
+80
src/cycle.gleam
···11+//// Module to conveniently run function in loop
22+////
33+//// Use-case for this are:
44+//// - Maintain a state while sharing resources easily
55+//// - Run operation forever
66+//// - Limit cycles a loop can go through
77+////
88+//// ```gleam
99+//// cycle.start(with: #([], 0), run: fn(state) {
1010+//// let #(is, i) = state
1111+//// case i < 5 {
1212+//// True -> {
1313+//// let i = i + 1
1414+//// cycle.continue(#(list.prepend(is, i), i))
1515+//// }
1616+//// _ -> cycle.stop(is)
1717+//// }
1818+//// })
1919+//// ```
2020+2121+/// Message to the cycler if the cycle should continue or stop with a result.
2222+pub type Next(state, result) {
2323+ /// Continue the cycle with new state.
2424+ Continue(state)
2525+ /// Stop the cycle with a result.
2626+ Stop(result)
2727+}
2828+2929+/// Convenient shortcut to `Continue`.
3030+pub fn continue(state: state) -> Next(state, result) {
3131+ Continue(state)
3232+}
3333+3434+/// Convenient shortcut to `Stop`.
3535+pub fn stop(result: result) -> Next(state, result) {
3636+ Stop(result)
3737+}
3838+3939+type LimitState(state) {
4040+ LimitState(nth_cycle: Int, state: state)
4141+}
4242+4343+/// Start a cycle with guard on max cycle, returns like normal `start`, otherwise `Error(Nil)` if n-th cycle has reached the max.
4444+pub fn safely_start(
4545+ with state: state,
4646+ max_cycle max: Int,
4747+ run function: fn(state) -> Next(state, result),
4848+) -> Result(result, Nil) {
4949+ // no one except me and god can understand this piece of snippet
5050+ // but now only god can understand whatever bullshit happens in here
5151+ start(LimitState(0, state), fn(state) {
5252+ case state.nth_cycle <= max {
5353+ True ->
5454+ case function(state.state) {
5555+ Continue(inner_state) ->
5656+ Continue(LimitState(
5757+ state: inner_state,
5858+ nth_cycle: state.nth_cycle + 1,
5959+ ))
6060+ Stop(result) -> stop(Ok(result))
6161+ }
6262+ _ -> stop(Error(Nil))
6363+ }
6464+ })
6565+}
6666+6767+/// Start a cycle, returns a result if function tell the cycler to stop.
6868+pub fn start(
6969+ with state: state,
7070+ run function: fn(state) -> Next(state, result),
7171+) -> result {
7272+ loop(state, function)
7373+}
7474+7575+fn loop(state: state, function: fn(state) -> Next(state, result)) -> result {
7676+ case function(state) {
7777+ Continue(new_state) -> loop(new_state, function)
7878+ Stop(result) -> result
7979+ }
8080+}