(** * Relators *)

(** With this infrastructure in place, we can define actual relators
  that cover the commonly used type constructors. There are two broad
  categories: those related to function types, and those derived from
  inductive types. *)

(** As a convention, we name relators and the associated monotonicity
  theorems by appending the suffix [_rel] to the name of original type
  and those of its constructors. Likewise, we use the suffix [_subrel]
  for monotonicity theorems that characterize the variance of
  corresponding relators with respect to [subrel], and the suffix
  [_relim] for an associated instance of [RElim]. *)

(** ** A note about universes *)

(** Our use of [subrel] illustrates the fact that we consider
  relations over relations, and use relators to build up relations
  over the types of our relators to characterize their variance.
  In other words: we relate all kind of higher-order things as a
  matter of course and make sure our library eats its own dog
  food. This makes it relatively easy to run into universe
  inconsistency issues. This note is to explain why those happen, fix
  the terminology, and formulate best practices to avoid such
  problems.

  The critical universe level in Coqrel is the universe [U] from which
  the arguments and return type of [rel] are taken (so that
  conceptually we will write [rel : U -> U -> U]). We will call
  "small types" the inhabitants of [U], and "large types" those taken
  from a universe strictly larger than [U]. At the moment, [U] is
  implicit, but at some point we may want declare an explicit
  [Universe] to aid debugging and enhance clarity.

  Generally speaking, [U] is a pretty large universe: for the most
  part, the world of relational things sits well above the world of
  the things we want relate, no matter how high-order those are.
  Even [rel A B] is still a small type, so that
  [@subrel : forall A B : U, rel (rel A B) (rel A B)]
  above didn't cause any issue, although the type of [rel] itself,
  namely [U -> U -> U : U+1], is of course a large type.

  With this in mind, consider the relational operator
  [rel_compose : forall A B C, rel A B -> rel B C -> rel A C].
  We wish to register a monotonicity property for [rel_compose],
  establishing that composing larger relations yields a larger
  composite relation. As usual, we express this property by
  establishing that [rel_compose] is related to itself by an
  appropriate relation. However, the type of [rel_compose] may involve
  [U : U+1] as the type of the arguments [A B C], so that the type of
  [rel_compose] may itself be forced into sort [U+1]. Consequently,
  attempting to define that relation will yield a universe
  inconsistency as soon as, say, [subrel] is used as an argument of
  [rel_compose].

  Fortunately, even if [A B C : U], the specialized form
  [rel_compose A B C] is still in the small type
  [rel A B -> rel B C -> rel A C : U]. Moreover, there is no
  relational structure that we want to capture regarding the type
  parameters [A B C], so that we don't lose anything by defining our
  monotonicity property for the specialized [rel_compose A B C],
  as long as the property itself is generic in the specific values of
  [A B C].

  This is why, as a general rule, we define the [foo_subrel]
  properties of our relators on partially applied versions thereof,
  and use a [foo_subrel_params] hint to make sure our monotonicity
  tactic can resolve them. *)

(** ** Relators for function types *)

(** *** Pointwise extension of a relation *)

(** One useful special case is the pointwise extension of a relation
  on the domain to the function type. This is equivalent to [eq ==> R],
  however with the formulation below we don't have consider two equal
  elements of the domain. *)

Definition arrow_pointwise_rel A {B1 B2}:
  rel B1 B2 -> rel (A -> B1) (A -> B2) :=
    fun RB f g => forall a, RB (f a) (g a).Notation "- ==> R" := (arrow_pointwise_rel _ R)
  (at level 55, right associativity) : rel_scope.Global Instance arrow_pointwise_subrel_params:
  Params (@arrow_pointwise_rel) 3.(** *** Dependent pointwise extension *)

(** Like we did for non-dependent functions, we can provide a simpler
  definition for the special case where [E] is [eq]. *)

Definition forall_pointwise_rel {V: Type} {FV1 FV2: V -> Type}:
    (forall v, rel (FV1 v) (FV2 v)) ->
    rel (forall v, FV1 v) (forall v, FV2 v) :=
  fun FE f g =>
    forall v, FE v (f v) (g v).Notation "'forallr' - @ v : V , FE" :=
  (forall_pointwise_rel (V := V) (fun v => FE))
  (v ident, at level 200).Notation "'forallr' - @ v , FE" :=
  (forall_pointwise_rel (fun v => FE))
  (v ident, at level 200).Notation "'forallr' - : V , FE" :=
  (forall_pointwise_rel (V := V) (fun _ => FE))
  (at level 200).Notation "'forallr' - , FE" :=
  (forall_pointwise_rel (fun _ => FE))
  (at level 200).

Lemma forall_pointwise_relim {V FV1 FV2} R f g v P Q: RElim (R v) (f v) (g v) P Q -> RElim (@forall_pointwise_rel V FV1 FV2 R) f g P Q.