Current section
Files
Jump to
Current section
Files
src/gens.erl
-module(gens).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-define(FILEPATH, "src/gens.gleam").
-export([get/1, gen/2, combine/2, from_list/1, list_repeat/1, from_lazy_list/1, while/1, forever/1, infinite/2, merge/3, chain/1, monad/0, from_stream/1, to_stream/1]).
-export_type([generator/2, generator_m/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC("================== Generator ==================\n").
-type generator(HXT, HXU) :: {generator,
HXU,
fun((HXU) -> gleam@option:option({HXT, HXU}))}.
-type generator_m(HXV) :: any() | {gleam_phantom, HXV}.
-file("src/gens.gleam", 24).
?DOC(
" Returns the next element of a generator and the updated gen\n"
" ```gleam\n"
" let counter =\n"
" Generator(state: 0, next: fn(c) { Some(#(c, c + 1)) })\n"
" case get(counter).0 {\n"
" None -> echo \"no more numbers\"\n"
" Some(x) -> echo x // -> 0\n"
" }\n"
" ```\n"
).
-spec get(generator(HXW, HXX)) -> {gleam@option:option(HXW),
generator(HXW, HXX)}.
get(G) ->
case (erlang:element(3, G))(erlang:element(2, G)) of
none ->
{none, G};
{some, {X, S}} ->
{{some, X}, {generator, S, erlang:element(3, G)}}
end.
-file("src/gens.gleam", 32).
?DOC(" Tail recursive function for gen\n").
-spec gen_acc(generator(HYD, HYE), integer(), list(HYD)) -> {list(HYD),
generator(HYD, HYE)}.
gen_acc(G, N, Ls) ->
case N > 0 of
false ->
{lists:reverse(Ls), G};
true ->
case get(G) of
{none, _} ->
{lists:reverse(Ls), G};
{{some, X}, G2} ->
gen_acc(G2, N - 1, [X | Ls])
end
end.
-file("src/gens.gleam", 54).
?DOC(
" Generates at most n elements and returns the updated gen\n"
" ```gleam\n"
" let counter =\n"
" Generator(state: 0, next: fn(c) { Some(#(c, c + 1)) })\n"
" let #(nums, _) = gen(counter, 5)\n"
" echo nums // -> [0, 1, 2, 3, 4]\n"
" ```\n"
).
-spec gen(generator(HYL, HYM), integer()) -> {list(HYL), generator(HYL, HYM)}.
gen(G, N) ->
gen_acc(G, N, []).
-file("src/gens.gleam", 70).
?DOC(
" Combines two generators into one, advancing them separately\n"
" ```gleam\n"
" let two_powers =\n"
" Generator(state: 1, next: fn(p) { Some(#(p, p * 2)) })\n"
" let bellow_three =\n"
" Generator(state: 0, next: fn(n) { Some(#(n < 3, n + 1)) })\n"
"\n"
" let z = combine(two_powers, bellow_three)\n"
" let #(res, _) = gen(z, 5)\n"
" echo res\n"
" // -> [#(1, True), #(2, True), #(4, True), #(8, False), #(16, False)]\n"
" ```\n"
).
-spec combine(generator(HYS, HYT), generator(HYW, HYX)) -> generator({HYS, HYW}, {HYT,
HYX}).
combine(G1, G2) ->
{generator,
{erlang:element(2, G1), erlang:element(2, G2)},
fun(State) ->
{State1, State2} = State,
Res1 = (erlang:element(3, G1))(State1),
Res2 = (erlang:element(3, G2))(State2),
case {Res1, Res2} of
{{some, {X, State3}}, {some, {Y, State4}}} ->
{some, {{X, Y}, {State3, State4}}};
{_, _} ->
none
end
end}.
-file("src/gens.gleam", 108).
?DOC(
" Generates the lists elements\n"
" ```gleam\n"
" let gen_fruit = from_list([\"apple\", \"banana\", \"orange\"])\n"
" let #(fruit1, gen_fruit2) = get(gen_fruit)\n"
" echo fruit1\n"
" // -> Some(\"apple\")\n"
" ```\n"
" ```gleam\n"
" let #(fruit2, gen_fruit3) = get(gen_fruit2)\n"
" echo fruit2\n"
" // -> Some(\"banana\")\n"
" ```\n"
" ```gleam\n"
" let #(fruit3, gen_fruit4) = get(gen_fruit3)\n"
" echo fruit3\n"
" // -> Some(\"orange\")\n"
" ```\n"
" ```gleam\n"
" let #(fruit4, _) = get(gen_fruit4)\n"
" echo fruit4\n"
" // -> None\n"
" ```\n"
).
-spec from_list(list(HZC)) -> generator(HZC, list(HZC)).
from_list(L) ->
{generator, L, fun(Ls) -> case Ls of
[] ->
none;
[X | Rest] ->
{some, {X, Rest}}
end end}.
-file("src/gens.gleam", 124).
?DOC(
" Generates the lists elements on repeat\n"
" ```gleam\n"
" let gen_fruit = list_repeat([\"apple\", \"banana\", \"orange\"])\n"
" let #(fruits, _) = gen(gen_fruit, 5)\n"
" echo fruits\n"
" // -> [\"apple\", \"banana\", \"orange\", \"apple\", \"banana\"]\n"
" ```\n"
).
-spec list_repeat(list(HZH)) -> generator(HZH, list(HZH)).
list_repeat(L) ->
{generator, L, fun(Ls) -> case Ls of
[] ->
none;
[X | Rest] ->
case Rest of
[] ->
{some, {X, L}};
_ ->
{some, {X, Rest}}
end
end end}.
-file("src/gens.gleam", 145).
?DOC(
" Conversion from **LazyList** to **Generator** \n"
" ```gleam\n"
" let infinite_list = lazy.new() |> lazy.drop(3) |> lazy.map(fn(x) { x * 10 })\n"
" let ten_gen = from_lazy_list(infinite_list)\n"
" let #(res, _) = gen(ten_gen, 10)\n"
" echo res\n"
" // -> [30, 40, 50, 60, 70, 80, 90, 100, 110, 120]\n"
" ```\n"
).
-spec from_lazy_list(gens@lazy:lazy_list(HZM)) -> generator(HZM, gens@lazy:lazy_list(HZM)).
from_lazy_list(L) ->
{generator, L, fun(Ls) -> case gens@lazy:take(Ls, 1) of
[] ->
none;
[X | _] ->
{some,
{X,
begin
_pipe = Ls,
gens@lazy:drop(_pipe, 1)
end}}
end end}.
-file("src/gens.gleam", 173).
?DOC(
" Generates a list of all the elements. \\\n"
" If the generator does not have a reachable end condition, then this function does not end!!!\n"
" ```gleam\n"
" let gen_ten =\n"
" Generator(5, fn(x) {\n"
" case x < 10 {\n"
" True -> Some(#(x, x + 2))\n"
" False -> None\n"
" }\n"
" })\n"
" echo while(gen_ten)\n"
" // -> [5, 7, 9]\n"
" ```\n"
" This function is the in verse of `from_list`\n"
" ```gleam\n"
" let gen_li = from_list([\"A\", \"B\", \"C\"])\n"
" echo while(gen_li)\n"
" // -> [\"A\", \"B\", \"C\"]\n"
" ```\n"
).
-spec while(generator(HZR, any())) -> list(HZR).
while(G) ->
case get(G) of
{none, _} ->
[];
{{some, X}, G2} ->
[X | while(G2)]
end.
-file("src/gens.gleam", 204).
?DOC(
" Conversion from **Generator** to **LazyList** \\\n"
" Since each element in the lazy list needs to be generated separately, this function can be very slow!!! (O(n^2))\n"
" ```gleam\n"
" let gen_nat = Generator(1, fn(c) { Some(#(c, c + 1)) })\n"
" let lazy_nat = forever(gen_nat)\n"
" echo lazy.take(lazy_nat, 5)\n"
" // -> [Some(1), Some(2), Some(3), Some(4), Some(5)]\n"
" ```\n"
" This function is the inverse of `from_lazy_list`\n"
" ```gleam\n"
" let lazy_odds =\n"
" lazy.new()\n"
" |> lazy.filter(int.is_odd)\n"
" |> lazy.map(int.to_string)\n"
" let gen_odds = from_lazy_list(lazy_odds)\n"
" let lazy_odds_2 = forever(gen_odds) |> lazy.map(option.lazy_unwrap(_, fn() { panic }))\n"
" \n"
" echo lazy.take(lazy_odds, 5)\n"
" // -> [\"1\", \"3\", \"5\", \"7\", \"9\"]\n"
" echo gen(gen_odds, 5) |> pair.first\n"
" // -> [\"1\", \"3\", \"5\", \"7\", \"9\"]\n"
" echo lazy.take(lazy_odds_2, 5)\n"
" // -> [\"1\", \"3\", \"5\", \"7\", \"9\"]\n"
" ```\n"
).
-spec forever(generator(HZW, any())) -> gens@lazy:lazy_list(gleam@option:option(HZW)).
forever(G) ->
_pipe = gens@lazy:new(),
_pipe@4 = gens@lazy:map(_pipe, fun(N) -> _pipe@1 = gen(G, N + 1),
_pipe@2 = gleam@pair:first(_pipe@1),
_pipe@3 = gleam@list:drop(_pipe@2, N - 1),
gleam@list:last(_pipe@3) end),
gens@lazy:map(_pipe@4, fun(Res) -> case Res of
{ok, X} ->
{some, X};
{error, _} ->
none
end end).
-file("src/gens.gleam", 223).
?DOC(
" Creates a generator with `no end condition`\n"
" ```gleam\n"
" let gen_nat = infinite(1, fn(x) { #(x, x + 1) })\n"
" echo gen(gen_nat, 5).0\n"
" // -> [1, 2, 3, 4, 5]\n"
" ```\n"
).
-spec infinite(IAC, fun((IAC) -> {IAD, IAC})) -> generator(IAD, IAC).
infinite(State, Next) ->
{generator, State, fun(X) -> {some, Next(X)} end}.
-file("src/gens.gleam", 237).
?DOC(
" **Merges** two `sorted` generators into one\n"
" ```gleam\n"
" let counter1 = Generator(0, fn(c) { Some(#(c, c + 1)) })\n"
" let counter2 = Generator(0, fn(c) { Some(#(c, c + 2)) })\n"
" let merged = merge(counter1, counter2, int.compare)\n"
" merged \n"
" |> gen(8) \n"
" |> echo\n"
" // -> #([0, 0, 1, 2, 2, 3, 4, 4], Generator(#(5, 6), fn() { ... }))\n"
" ```\n"
).
-spec merge(
generator(IAG, IAH),
generator(IAG, IAK),
fun((IAG, IAG) -> gleam@order:order())
) -> generator(IAG, {IAH, IAK}).
merge(G1, G2, Comp) ->
{generator,
{erlang:element(2, G1), erlang:element(2, G2)},
fun(S) ->
{State1, State2} = S,
case {(erlang:element(3, G1))(State1),
(erlang:element(3, G2))(State2)} of
{{some, {X1, St1}}, {some, {X2, St2}}} ->
case Comp(X1, X2) of
gt ->
{some, {X2, {State1, St2}}};
_ ->
{some, {X1, {St1, State2}}}
end;
{{some, {X1@1, St1@1}}, none} ->
{some, {X1@1, {St1@1, State2}}};
{none, {some, {X2@1, St2@1}}} ->
{some, {X2@1, {State1, St2@1}}};
{none, none} ->
none
end
end}.
-file("src/gens.gleam", 261).
-spec chain_tail_rec(
list(IAP),
list(fun((IAP) -> gleam@option:option({IAR, IAP}))),
{gleam@option:option(IAR), list(IAP)}
) -> {gleam@option:option(IAR), list(IAP)}.
chain_tail_rec(States, Nexts, Acc) ->
case Acc of
{{some, X}, State_list} ->
case States of
[St | Rest_states] ->
chain_tail_rec(
Rest_states,
[],
{{some, X}, [St | State_list]}
);
[] ->
{{some, X}, lists:reverse(State_list)}
end;
{none, State_list@1} ->
case {States, Nexts} of
{[St@1 | Rest_states@1], [Nx | Rest_nexts]} ->
case Nx(St@1) of
{some, {X@1, New_st}} ->
chain_tail_rec(
Rest_states@1,
[],
{{some, X@1}, [New_st | State_list@1]}
);
none ->
chain_tail_rec(
Rest_states@1,
Rest_nexts,
{none, [St@1 | State_list@1]}
)
end;
{_, _} ->
{none, lists:reverse(State_list@1)}
end
end.
-file("src/gens.gleam", 312).
?DOC(
" **Chains** a list of generators\n"
" ```gleam\n"
" let gen_three =\n"
" Generator(1, fn(x) {\n"
" case x <= 3 {\n"
" True -> Some(#(x, x + 1))\n"
" False -> None\n"
" }\n"
" })\n"
" let gen_nat = infinite(1, fn(x) { #(x, x + 1) })\n"
" // Once the first generator ends, the second one begins\n"
" let gen_chain = chain([gen_three, gen_nat])\n"
" gen_chain\n"
" |> gen(8)\n"
" |> pair.first\n"
" // -> [1, 2, 3, 1, 2, 3, 4, 5]\n"
" ```\n"
).
-spec chain(list(generator(IAY, IAZ))) -> generator(IAY, list(IAZ)).
chain(Generators) ->
{generator,
begin
_pipe = Generators,
gleam@list:map(_pipe, fun(G) -> erlang:element(2, G) end)
end,
fun(Gen_states) ->
Gen_nexts = begin
_pipe@1 = Generators,
gleam@list:map(_pipe@1, fun(G@1) -> erlang:element(3, G@1) end)
end,
case chain_tail_rec(Gen_states, Gen_nexts, {none, []}) of
{none, _} ->
none;
{{some, X}, States} ->
{some, {X, States}}
end
end}.
-file("src/gens.gleam", 339).
?DOC(
" `Monad` instance for `Generator` type\n"
" ```gleam\n"
" let plus_one = infinite(1, fn(x) { #(x, x + 1) })\n"
" let plus_two = infinite(1, fn(x) { #(x, x + 2) })\n"
" let g = {\n"
" use x <- monad().bind(plus_one)\n"
" echo x // -> 1, 4, 7, 10, 13..\n"
" use y <- monad().map(plus_two)\n"
" echo y // -> 2, 5, 8, 9, 12..\n"
" x + y\n"
" }\n"
" g |> gen(5) |> pair.first |> echo\n"
" // -> [3, 9, 15, 21, 27]\n"
" ```\n"
).
-spec monad() -> cat@monad:monad(generator_m(IBG), IBI, IBJ, generator(IBI, IBG), generator(IBJ, IBG)).
monad() ->
{monad, fun(_) -> erlang:error(#{gleam_error => panic,
message => <<"`panic` expression evaluated."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gens"/utf8>>,
function => <<"monad"/utf8>>,
line => 341}) end, fun(G, F) ->
{generator,
erlang:element(2, G),
fun(State) -> case (erlang:element(3, G))(State) of
none ->
none;
{some, {X, New_state}} ->
(erlang:element(3, F(X)))(New_state)
end end}
end, fun(G@1, F@1) ->
{generator,
erlang:element(2, G@1),
fun(State@1) -> case (erlang:element(3, G@1))(State@1) of
none ->
none;
{some, {X@1, New_state@1}} ->
{some, {F@1(X@1), New_state@1}}
end end}
end}.
-file("src/gens.gleam", 394).
?DOC(
" Conversion from **Stream** to **Generator** \\\n"
" Helper Stream\n"
" ```gleam\n"
" pub fn dummy() -> Stream(Nil) {\n"
" Stream(head: fn() { Nil }, tail: dummy)\n"
" }\n"
" ```\n"
" Fibonacci Stream\n"
" ```gleam\n"
" let fibo_s =\n"
" dummy()\n"
" |> stream.scan(#(1, 1), fn(_, int_pair) {\n"
" case int_pair {\n"
" #(x, y) -> #(y, x + y)\n"
" }\n"
" })\n"
" |> stream.map(fn(int_pair) { int_pair.1 })\n"
" \n"
" fibo_s\n"
" |> stream.take(5)\n"
" |> echo\n"
" // -> [1, 2, 3, 5, 8]\n"
" ```\n"
" Fibonacci Generator\n"
" ```gleam\n"
" let fibo_g = from_stream(fibo_s)\n"
" \n"
" fibo_g\n"
" |> gen(5)\n"
" |> pair.first\n"
" |> echo\n"
" // -> [1, 2, 3, 5, 8]\n"
" ```\n"
).
-spec from_stream(gens@stream:stream(IBT)) -> generator(IBT, gens@stream:stream(IBT)).
from_stream(Stream) ->
{generator,
Stream,
fun(S) ->
{some, {(erlang:element(2, S))(), (erlang:element(3, S))()}}
end}.
-file("src/gens.gleam", 424).
?DOC(
" Conversion from **Generator** to **Stream** \\\n"
" Fibonacci Generator\n"
" ```gleam\n"
" let fibo_g =\n"
" Generator(state: #(1, 1), next: fn(int_pair) {\n"
" case int_pair {\n"
" #(x, y) -> Some(#(y, #(y, x + y)))\n"
" }\n"
" })\n"
"\n"
" fibo_g\n"
" |> gen(5)\n"
" |> pair.first\n"
" |> echo\n"
" // -> [1, 2, 3, 5, 8]\n"
" ```\n"
" Fibonacci Stream\n"
" ```gleam\n"
" let fibo_s = to_stream(fibo_g)\n"
"\n"
" fibo_s\n"
" |> stream.map(option.unwrap(_, -1))\n"
" |> stream.take(5)\n"
" |> echo\n"
" // -> [1, 2, 3, 5, 8]\n"
" ```\n"
).
-spec to_stream(generator(IBY, any())) -> gens@stream:stream(gleam@option:option(IBY)).
to_stream(Generator) ->
{stream, fun() -> case get(Generator) of
{X, _} ->
X
end end, fun() -> case get(Generator) of
{_, Next_gen} ->
to_stream(Next_gen)
end end}.