define combine : (fn <T,U> ((fn T -> Option<U>), (fn T -> Option<U>)) -> (fn T -> Option<U>)) = generic T, U {
      fun f:(fn T -> Option<U>), g:(fn T -> Option<U>) {
        fun x:T {
          if f(x) = none then
            g(x)
          else
            f(x)
        }
      }
    }

combine_left: (all T:type, U:type, f:(fn T -> Option<U>), g:(fn T -> Option<U>), x:T. (if g(x) = none then combine(f, g)(x) = f(x)))

combine_right: (all T:type, U:type, f:(fn T -> Option<U>), g:(fn T -> Option<U>), x:T. (if f(x) = none then combine(f, g)(x) = g(x)))

define domain : (fn <T,U> (fn T -> Option<U>) -> Set<T>) = generic T, U {
      fun f:(fn T -> Option<U>) {
        set_of_pred(fun x:T { switch f(x) { case none { false } case just(y) { true } } })
      }
    }

define empty_map : (fn <T,U> T -> Option<U>) = generic T, U {
      fun k:T {
        @none<U>
      }
    }

define flip : (fn <T,U,V> (fn (T, U) -> V) -> (fn (U, T) -> V)) = generic T, U, V {
      fun f {
        fun x, y {
          f(y, x)
        }
      }
    }

flip_flip: (all T:type. (all f:(fn (T, T) -> T). flip(flip(f)) = f))

define operator ∘ : (fn <T,U,V> ((fn U -> V), (fn T -> U)) -> (fn T -> V)) = generic T, U, V {
      fun g:(fn U -> V), f:(fn T -> U) {
        fun x:T {
          g(f(x))
        }
      }
    }

define restrict : (fn <T,U> ((fn T -> Option<U>), Set<T>) -> (fn T -> Option<U>)) = generic T, U {
      fun f:(fn T -> Option<U>), P:Set<T> {
        fun x:T {
          if x  P then
            f(x)
          else
            @none<U>
        }
      }
    }

restrict_domain: (all T:type, U:type. (all f:(fn T -> Option<U>), P:Set<T>. domain(restrict(f, P))  P))

define update : (fn <T,U> ((fn T -> U), T, U) -> (fn T -> U)) = generic T, U {
      fun f:(fn T -> U), x:T, v:U {
        fun y:T {
          if y = x then
            v
          else
            f(y)
        }
      }
    }

update_eq: (all T:type, U:type. (all f:(fn T -> U), x:T, v:U. update(f, x, v)(x) = v))

update_not_eq: (all T:type, U:type. (all f:(fn T -> U), x:T, v:U, y:T. (if x  y then update(f, x, v)(y) = f(y))))

update_permute: (all T:type, U:type. (all f:(fn T -> U), x:T, v:U, w:U, y:T. (if x  y then update(update(f, x, v), y, w) = update(update(f, y, w), x, v))))

update_shadow: (all T:type, U:type. (all f:(fn T -> U), x:T, v:U, w:U. update(update(f, x, v), x, w) = update(f, x, w)))