I've been programming in Coq for a few months now. Particularly, I'm interested in functional programming proofs where functions arise everywhere (optics, state monad, etc.). In this sense, dealing with functional extensionality has become essential, though extremely annoying. To illustrate the situation, let us assume a simplication of Monad
(just one law defined):
Class Monad (m : Type -> Type) :=
{ ret : forall {X}, X -> m X
; bind : forall {A B}, m A -> (A -> m B) -> m B
}.
Notation "ma >>= f" := (bind ma f) (at level 50, left associativity).
Class MonadLaws m `{Monad m} :=
{ right_id : forall X (p : m X), p >>= ret = p }.
Now, I'd like to prove that chaining several ret
s to a program p
should be equivalent to the very same program:
Example ugly_proof :
forall m `{MonadLaws m} A (p : m A),
p >>= (fun a1 => ret a1 >>= (fun a2 => ret a2 >>= ret)) = p.
Proof.
intros.
destruct H0 as [r_id].
assert (J : (fun a2 : A => ret a2 >>= ret) =
(fun a2 : A => ret a2)).
{ auto using functional_extensionality. }
rewrite J.
assert (K : (fun a1 : A => ret a1 >>= (fun a2 : A => ret a2)) =
(fun a1 : A => ret a1)).
{ auto using functional_extensionality. }
rewrite K.
now rewrite r_id.
Qed.
The proof is correct, but the iterative assert
pattern makes it very cumbersome. Indeed, it becomes impractical when things get more complex, as you can find in this proof (which evidences that an affine traversal is closed under composition).
Having said so, what is the best way of implementing the aforementioned proof? Is there any project or article (more approachable than this one) around functional extensionality to be taken as reference?