///|
pub(open) trait Length {
  length(Self) -> Int
  is_empty(Self) -> Bool = _
}

///|
impl Length with is_empty(self) {
  self.length() == 0
}

///|
pub fn[T : Length] is_empty(target : T) -> Bool {
  target.is_empty()
}

///|
pub fn[T : Length] is_not_empty(target : T) -> Bool {
  not(target.is_empty())
}

///|
pub impl[A] Length for Array[A] with length(self) {
  self |> Array::length()
}

///|
pub impl[A] Length for FixedArray[A] with length(self) {
  self |> FixedArray::length()
}

///|
pub impl Length for String with length(self) = "%string_length"

///|
pub impl Length for Bytes with length(self) = "%bytes_length"

///|
pub impl[K, V] Length for Map[K, V] with length(self) {
  self.size()
}

///|
pub impl[K, V] Length for Map[K, V] with is_empty(self) {
  self.is_empty()
}

///|
pub impl[V] Length for V? with length(self) {
  if self.is_empty() {
    0
  } else {
    1
  }
}

///|
pub impl[V] Length for V? with is_empty(self) {
  self.is_empty()
}

///|
test "map" {
  let empty : Map[Int, Int] = {}
  assert_true(is_empty(empty))
  assert_false(is_empty({ "a": 0 }))
  assert_true(is_empty(""))
  assert_false(is_empty(" "))
}