///|
pub(all) type Foldable[R, A, B] (R, B, (B, A) -> B) -> B

///|
pub fn[A, B] Foldable::array() -> Foldable[Array[A], A, B] {
  fn(array, init, f) { array.fold(init~, f) }
}

///|
pub fn[A, B] Foldable::fixed_array() -> Foldable[FixedArray[A], A, B] {
  fn(array, init, f) { array.fold(init~, f) }
}

///|
pub fn[A, B] Foldable::many() -> Foldable[Many[A], A, B] {
  fn(array, init, f) { array.fold(init~, f) }
}

///|
pub(all) type FoldableWithIndex[R, A, B] (R, B, (Int, B, A) -> B) -> B

///|
pub fn[A, B] FoldableWithIndex::array() -> FoldableWithIndex[Array[A], A, B] {
  fn(array, init, f) { array.foldi(init~, f) }
}

///|
pub fn[A, B] FoldableWithIndex::fixed_array() -> FoldableWithIndex[
  FixedArray[A],
  A,
  B,
] {
  fn(array, init, f) { array.foldi(init~, f) }
}

///|
pub fn[A, B] FoldableWithIndex::many() -> FoldableWithIndex[Many[A], A, B] {
  fn(array, init, f) { array.foldi(init~, f) }
}

///|
pub(all) struct Sequence[R, I, A] {
  make : (Int, A) -> R
  op_get : (R, I) -> A
  op_set : (R, I, A) -> Unit
}

///|
pub fn[T1 : Length, A, B, T2 : Default] Indexable::sequence_transform(
  self : Indexable[T1, A],
  input : T1,
  f : (A) -> B,
  target~ : Indexable[T2, B]
) -> T2 {
  let len = input.length()
  if len == 0 {
    return T2::default()
  }
  let a0 = self.get(input, 0)
  let rb = target.make(len, init=f(a0))
  for i in 1.. f)
  }
  rb
}

///|
pub fn[RA : Length, RB : Default, RC : Default, A, B, C] Indexable::separated(
  self : Indexable[RA, A],
  fb~ : Indexable[RB, B],
  fc~ : Indexable[RC, C],
  input : RA,
  f : (A) -> (B, C)
) -> (RB, RC) {
  let len = input.length()
  if len == 0 {
    return (RB::default(), RC::default())
  }
  let reader = self.get(input, 0)
  let e = f(reader)
  let left = fb.make(len, init=e.0)
  let right = fc.make(len, init=e.1)
  for i in 1.. R {
  R::default()
}

///|
pub fn[R, I, A] Sequence::make(
  self : Sequence[R, I, A],
  len : Int,
  init~ : A
) -> R {
  (self.make)(len, init)
}

///|
pub fn[R, I, A] Sequence::get(self : Sequence[R, I, A], r : R, i : I) -> A {
  (self.op_get)(r, i)
}

///|
pub fn[R, I, A] Sequence::set(
  self : Sequence[R, I, A],
  r : R,
  i : I,
  a : A
) -> Unit {
  (self.op_set)(r, i, a)
}

///|
pub fn[A] Sequence::array() -> Sequence[Array[A], Int, A] {
  { make: Array::make, op_get: Array::op_get, op_set: Array::op_set }
}

///|
pub fn[A] Sequence::fixed_array() -> Sequence[FixedArray[A], Int, A] {
  {
    make: FixedArray::make,
    op_get: FixedArray::op_get,
    op_set: FixedArray::op_set,
  }
}

///|
pub fn[A] Sequence::many() -> Sequence[Many[A], Int, A] {
  { make: Many::make, op_get: Many::op_get, op_set: Many::op_set }
}