gens

================== Generator ==================

Types

pub type Generator(a, s) {
  Generator(state: s, next: fn(s) -> option.Option(#(a, s)))
}

Constructors

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"]
Search Document