(* This file gives an elementary example of how to write a coinductive predicate over itrees and use it to do some simple proofs. This is a common use case when working with itrees. *)
(* TODO: this infrastructure should be generalized and integrated into the library. *)
Set Implicit Arguments.
Set Contextual Implicit.
Classes.Morphisms
ProofIrrelevance.
Monads.
ITree
ITreeFacts.
(* Defining an itree interpreter -------------------------------------------- *)
(* Suppose that we have an ITree whose event type admits certain kinds of operations.  For this example, we take a simplified version of the [stateE] event, which defines [get] and [put] operations on states. *)
Variant stateE (S:Type) : Type -> Type :=
| Get : stateE S S
| Put : S -> stateE S unit.
(* We can define an interpretation of [stateE] events into itrees with no events as follows. Note that we split out the "node functor", which is parameterized by the interpreter on the recursive calls, separating it from the CoFixpoint.  This structure mirrors the way that we define predicates below.  The ITrees library defines more elaborate versions of these interpreters that work with multiple events types. *)
Definition stateT (S:Type) (M:Type -> Type) (R:Type) := S -> M (S * R)%type.
Definition interpret_stateF (S:Type) {R}
(rec : itree (stateE S) R -> stateT S (itree void1) R)
(t : itree (stateE S) R) : stateT S (itree void1) R :=
fun s => match observe t with
| RetF r => ret (s, r)
| TauF t =>  Tau (rec t s)
| VisF Get k => Tau (rec (k s) s)
| VisF (Put s') k => Tau (rec (k tt) s')
end.
CoFixpoint interpret_state {S R:Type} (t:itree (stateE S) R) : stateT S (itree void1) R :=
interpret_stateF interpret_state t.

Lemma unfold_interpret_state : forall {S R} (t : itree (stateE S) R) (s:S),
observe (interpret_state t s) =
observe (interpret_stateF interpret_state t s).