(** * Introduction to interaction trees *)
(** This file contains exercises to get familiar with the Interaction Trees library, via the simplified interface of [theories/ITree/Simple.v].  The solutions can be found in [examples/Introduction.v]. *)
(* HIDE *)
(* This is actually the solutions file. To do the exercises, see [tutorial/Introduction.v]. *)
(* /HIDE *)
(* begin hide *)
Arith
Lia
List.
Monad
Traversable
Data.List.
Simple.
Open Scope monad_scope.
(* end hide *)
(** * Events *)
(** We first show how to represent effectful computations as interaction trees. The [itree] type is parameterized by an _event type_, which is typically an indexed type with constructors describing the possible interactions with the environment.  For instance, [ioE] below is a type of simple input/output events. The fields of each constructor contain data produced by the tree, and the type index the type of answer that the tree expects as a response to the event. *)
Inductive ioE : Type -> Type :=
| Input : ioE (list nat)
(** Ask for a list of [nat] from the environment. *)
| Output : list nat -> ioE unit
(** Send a list of [nat]. *)
.
(** Events are wrapped as ITrees using [ITree.trigger], and composed using monadic operations [bind] and [ret]. *)
(** Read some input, and echo it back, appending [1] to it. *)
Definition write_one : itree ioE unit :=
xs <- ITree.trigger Input;;
ITree.trigger (Output (xs ++ [1])).
(** We can _interpret_ interaction trees by giving semantics to their events individually, as a _handler_: a function of type [forall R, ioE R -> M R] for some monad [M]. *)
(** The handler [handle_io] below responds to every [Input] event with [[0]], and appends any [Output] to a global log.  Here the target monad is [M := stateT (list nat) (itree void1)], - [stateT] is the state monad transformer, that type unfolds to [M R := list nat -> itree void1 (list nat * R)]; - [void1] is the empty event (so the resulting ITree can trigger no event). *)
Compute Monads.stateT (list nat) (itree void1) unit.
Print void1.
Definition handle_io
: forall R, ioE R -> Monads.stateT (list nat) (itree void1) R
:= fun R e log =>
match e with
| Input => ret (log, [0])
| Output o => ret (log ++ o, tt)
end.
(** [interp] lifts any handler into an _interpreter_, of type [forall R, itree ioE R -> M R]. *)
Definition interp_io
: forall R, itree ioE R -> itree void1 (list nat * R)
:= fun R t => interp handle_io t [].
(** We can now interpret [write_one]. *)
Definition interpreted_write_one : itree void1 (list nat * unit)
:= interp_io _ write_one.

(** Intuitively, [interp_io] replaces every [ITree.trigger] in the definition of [write_one] with [handle_io]: [[ interpreted_write_one = xs <- handle_io _ Input;; handle_io _ (Output (xs ++ [1])) ]]  We can prove such a lemma in a more restricted setting, where [handle_io] targets some monad of the form [itree F], rather than [T (itree F)] (above, [T := stateT (list nat)]). (The library is currently missing some theory about the monads we can instantiate [interp] with.)  The proof of [interp_write_one] will require us to rewrite an expression under a binder---i.e., on the right side of a [;;]. The [rewrite] tactic will fail in this situation; instead, we can use [setoid_rewrite], which works under binders. *)
Lemma interp_write_one F (handle_io : forall R, ioE R -> itree F R)
: interp handle_io write_one
≈ (xs <- handle_io _ Input;;
handle_io _ (Output (xs ++ [1]))).