///|
fn[Message] reconcile_widget(
  app : @gtk.Application,
  dispatch : (Message) -> Unit,
  old_widget : IWidget,
  new_widget : VWidget[Message],
) -> IWidget {
  match old_widget.reconcile(new_widget, app, dispatch) {
    Some(updated) => updated
    None => {
      old_widget.destroy()
      new_widget.create(app, dispatch)
    }
  }
}

///|
fn[Message] reconcile_array(
  app : @gtk.Application,
  dispatch : (Message) -> Unit,
  old_children : Array[IWidget],
  new_children : Array[VWidget[Message]],
  remove~ : (IWidget) -> Unit,
  append~ : (IWidget) -> Unit,
) -> Array[IWidget] {
  let result : Array[IWidget] = []
  let min_len = if old_children.length() < new_children.length() {
    old_children.length()
  } else {
    new_children.length()
  }
  for i in 0.. result.push(updated)
      None => {
        remove(old_children[i])
        old_children[i].destroy()
        let new_w = new_children[i].create(app, dispatch)
        append(new_w)
        result.push(new_w)
      }
    }
  }
  for i in min_len.. Unit,
  old_children : Map[String, IWidget],
  new_children : Map[String, VWidget[Message]],
  remove~ : (IWidget) -> Unit,
  append~ : (IWidget) -> Unit,
) -> Map[String, IWidget] {
  let result : Map[String, IWidget] = Map([])
  for old_key_child in old_children {
    let (key, old_child) = old_key_child
    if new_children.get(key) is Some(new_child) {
      match old_child.reconcile(new_child, app, dispatch) {
        Some(updated) => result.set(key, updated)
        None => {
          remove(old_child)
          old_child.destroy()
          let new_w = new_child.create(app, dispatch)
          append(new_w)
          result.set(key, new_w)
        }
      }
    } else {
      remove(old_child)
      old_child.destroy()
    }
  }
  for new_key_child in new_children {
    let (key, new_child) = new_key_child
    if !old_children.contains(key) {
      let new_widget = new_child.create(app, dispatch)
      append(new_widget)
      result.set(key, new_widget)
    }
  }
  result
}