///|
/// A pool of reusable objects: a caller takes one, uses it, and hands it back.
/// Building is deferred until the first take, so a pool nobody touches costs
/// nothing — which is what makes it usable for a buffer or a connection that only
/// some code paths need.
pub struct Pool[T] {
  make : () -> T
  idle : Array[T]
}

///|
/// A pool that builds its objects with `make`.
pub fn[T] Pool::new(make : () -> T) -> Pool[T] {
  { make, idle: [], }
}

///|
/// Take an object: an idle one when there is one, a fresh one otherwise.
pub fn[T] Pool::get(self : Pool[T]) -> T {
  match self.idle.pop() {
    Some(item) => item
    None => (self.make)()
  }
}

///|
/// Hand an object back. The pool keeps every one it is given, so a caller that
/// wants a ceiling on retained objects puts one on its own side.
pub fn[T] Pool::put(self : Pool[T], item : T) -> Unit {
  self.idle.push(item)
}

///|
/// How many objects are idle.
pub fn[T] Pool::idle(self : Pool[T]) -> Int32 {
  self.idle.length()
}