gens
================== Generator ==================
Types
pub type Generator(a, s) {
Generator(state: s, next: fn(s) -> option.Option(#(a, s)))
}
Constructors
-
Generator(state: s, next: fn(s) -> option.Option(#(a, s)))
Phantom type for Generator cat instances
pub type GeneratorM(s)
Values
pub fn chain(
generators: List(Generator(a, s)),
) -> Generator(a, List(s))
Chains a list of generators
let gen_three =
Generator(1, fn(x) {
case x <= 3 {
True -> Some(#(x, x + 1))
False -> None
}
})
let gen_nat = infinite(1, fn(x) { #(x, x + 1) })
// Once the first generator ends, the second one begins
let gen_chain = chain([gen_three, gen_nat])
gen_chain
|> gen(8)
|> pair.first
// -> [1, 2, 3, 1, 2, 3, 4, 5]
pub fn combine(
g1: Generator(a, s1),
g2: Generator(b, s2),
) -> Generator(#(a, b), #(s1, s2))
Combines two generators into one, advancing them separately
let two_powers =
Generator(state: 1, next: fn(p) { Some(#(p, p * 2)) })
let bellow_three =
Generator(state: 0, next: fn(n) { Some(#(n < 3, n + 1)) })
let z = combine(two_powers, bellow_three)
let #(res, _) = gen(z, 5)
echo res
// -> [#(1, True), #(2, True), #(4, True), #(8, False), #(16, False)]
pub fn forever(
g: Generator(a, s),
) -> lazy.LazyList(option.Option(a))
Conversion from Generator to LazyList
Since each element in the lazy list needs to be generated separately, this function can be very slow!!! (O(n^2))
let gen_nat = Generator(1, fn(c) { Some(#(c, c + 1)) })
let lazy_nat = forever(gen_nat)
echo lazy.take(lazy_nat, 5)
// -> [Some(1), Some(2), Some(3), Some(4), Some(5)]
This function is the inverse of from_lazy_list
let lazy_odds =
lazy.new()
|> lazy.filter(int.is_odd)
|> lazy.map(int.to_string)
let gen_odds = from_lazy_list(lazy_odds)
let lazy_odds_2 = forever(gen_odds) |> lazy.map(option.lazy_unwrap(_, fn() { panic }))
echo lazy.take(lazy_odds, 5)
// -> ["1", "3", "5", "7", "9"]
echo gen(gen_odds, 5) |> pair.first
// -> ["1", "3", "5", "7", "9"]
echo lazy.take(lazy_odds_2, 5)
// -> ["1", "3", "5", "7", "9"]
pub fn from_lazy_list(
l: lazy.LazyList(a),
) -> Generator(a, lazy.LazyList(a))
Conversion from LazyList to Generator
let infinite_list = lazy.new() |> lazy.drop(3) |> lazy.map(fn(x) { x * 10 })
let ten_gen = from_lazy_list(infinite_list)
let #(res, _) = gen(ten_gen, 10)
echo res
// -> [30, 40, 50, 60, 70, 80, 90, 100, 110, 120]
pub fn from_list(l: List(a)) -> Generator(a, List(a))
Generates the lists elements
let gen_fruit = from_list(["apple", "banana", "orange"])
let #(fruit1, gen_fruit2) = get(gen_fruit)
echo fruit1
// -> Some("apple")
let #(fruit2, gen_fruit3) = get(gen_fruit2)
echo fruit2
// -> Some("banana")
let #(fruit3, gen_fruit4) = get(gen_fruit3)
echo fruit3
// -> Some("orange")
let #(fruit4, _) = get(gen_fruit4)
echo fruit4
// -> None
pub fn from_stream(
stream: stream.Stream(a),
) -> Generator(a, stream.Stream(a))
Conversion from Stream to Generator
Helper Stream
pub fn dummy() -> Stream(Nil) {
Stream(head: fn() { Nil }, tail: dummy)
}
Fibonacci Stream
let fibo_s =
dummy()
|> stream.scan(#(1, 1), fn(_, int_pair) {
case int_pair {
#(x, y) -> #(y, x + y)
}
})
|> stream.map(fn(int_pair) { int_pair.1 })
fibo_s
|> stream.take(5)
|> echo
// -> [1, 2, 3, 5, 8]
Fibonacci Generator
let fibo_g = from_stream(fibo_s)
fibo_g
|> gen(5)
|> pair.first
|> echo
// -> [1, 2, 3, 5, 8]
pub fn gen(
g: Generator(a, s),
n: Int,
) -> #(List(a), Generator(a, s))
Generates at most n elements and returns the updated gen
let counter =
Generator(state: 0, next: fn(c) { Some(#(c, c + 1)) })
let #(nums, _) = gen(counter, 5)
echo nums // -> [0, 1, 2, 3, 4]
pub fn get(
g: Generator(a, s),
) -> #(option.Option(a), Generator(a, s))
Returns the next element of a generator and the updated gen
let counter =
Generator(state: 0, next: fn(c) { Some(#(c, c + 1)) })
case get(counter).0 {
None -> echo "no more numbers"
Some(x) -> echo x // -> 0
}
pub fn infinite(
state: s,
next: fn(s) -> #(a, s),
) -> Generator(a, s)
Creates a generator with no end condition
let gen_nat = infinite(1, fn(x) { #(x, x + 1) })
echo gen(gen_nat, 5).0
// -> [1, 2, 3, 4, 5]
pub fn list_repeat(l: List(a)) -> Generator(a, List(a))
Generates the lists elements on repeat
let gen_fruit = list_repeat(["apple", "banana", "orange"])
let #(fruits, _) = gen(gen_fruit, 5)
echo fruits
// -> ["apple", "banana", "orange", "apple", "banana"]
pub fn merge(
g1: Generator(a, s1),
g2: Generator(a, s2),
comp: fn(a, a) -> order.Order,
) -> Generator(a, #(s1, s2))
Merges two sorted generators into one
let counter1 = Generator(0, fn(c) { Some(#(c, c + 1)) })
let counter2 = Generator(0, fn(c) { Some(#(c, c + 2)) })
let merged = merge(counter1, counter2, int.compare)
merged
|> gen(8)
|> echo
// -> #([0, 0, 1, 2, 2, 3, 4, 4], Generator(#(5, 6), fn() { ... }))
pub fn monad(
,
) -> monad.Monad(
GeneratorM(s),
a,
b,
Generator(a, s),
Generator(b, s),
)
Monad instance for Generator type
let plus_one = infinite(1, fn(x) { #(x, x + 1) })
let plus_two = infinite(1, fn(x) { #(x, x + 2) })
let g = {
use x <- monad().bind(plus_one)
echo x // -> 1, 4, 7, 10, 13..
use y <- monad().map(plus_two)
echo y // -> 2, 5, 8, 9, 12..
x + y
}
g |> gen(5) |> pair.first |> echo
// -> [3, 9, 15, 21, 27]
pub fn to_stream(
generator: Generator(a, s),
) -> stream.Stream(option.Option(a))
Conversion from Generator to Stream
Fibonacci Generator
let fibo_g =
Generator(state: #(1, 1), next: fn(int_pair) {
case int_pair {
#(x, y) -> Some(#(y, #(y, x + y)))
}
})
fibo_g
|> gen(5)
|> pair.first
|> echo
// -> [1, 2, 3, 5, 8]
Fibonacci Stream
let fibo_s = to_stream(fibo_g)
fibo_s
|> stream.map(option.unwrap(_, -1))
|> stream.take(5)
|> echo
// -> [1, 2, 3, 5, 8]
pub fn while(g: Generator(a, s)) -> List(a)
Generates a list of all the elements.
If the generator does not have a reachable end condition, then this function does not end!!!
let gen_ten =
Generator(5, fn(x) {
case x < 10 {
True -> Some(#(x, x + 2))
False -> None
}
})
echo while(gen_ten)
// -> [5, 7, 9]
This function is the in verse of from_list
let gen_li = from_list(["A", "B", "C"])
echo while(gen_li)
// -> ["A", "B", "C"]