///|
pub impl Show for Environment with fn output(self, logger) {
  logger.write_string("+frame")
  match self.closure {
    Some({ name: Some(name), .. }) => logger.write_string("(\{name})")
    None => logger.write_string("(global)")
    _ => ()
  }
  logger.write_string(":\n")
  for k, v in self.binds {
    logger
    ..write_string("| ")
    ..write_object(k)
    ..write_string(" : ")
    ..write_object(v)
    .write_char('\n')
  }
  match self.next {
    None => logger.write_string("+--------")
    Some(x) => logger.write_object(x)
  }
}

///|
pub fn Environment::lookup(
  self : Environment,
  name : Symbol,
) -> Value raise SchemeException {
  match self.binds.get(name) {
    Some(x) => x
    None =>
      match self.next {
        Some(next) => next.lookup(name)
        None => raise UndefineVar(name)
      }
  }
}

///|
pub fn Environment::set_var(
  self : Environment,
  name : Symbol,
  new_value : Value,
) -> Unit raise SchemeException {
  match self.binds.get(name) {
    Some(_) => self.binds[name] = new_value
    None =>
      match self.next {
        Some(next) => next.set_var(name, new_value)
        None => raise UndefineVar(name)
      }
  }
}

///|
pub fn Environment::define_var(
  self : Environment,
  name : Symbol,
  new_value : Value,
) -> Unit {
  self.binds[name] = new_value
}

///|
pub fn Environment::define_vars(
  self : Environment,
  name_value : ReadOnlyArray[(Symbol, Value)],
) -> Unit {
  for nv in name_value {
    self.binds[nv.0] = nv.1
  }
}

///|
pub fn Environment::extend_(
  self : Environment,
  names : FixedArray[Symbol],
  values : FixedArray[Value],
  closure : Closure,
) -> Environment {
  let binds = @hashmap.HashMap([], capacity=names.length())
  values.eachi(fn(i, v) { binds[names[i]] = v })
  { binds, next: Some(self), closure: Some(closure) }
}

///|
pub fn Environment::base() -> Environment {
  { next: None, binds: @hashmap.HashMap([]), closure: None }
}