(** * 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]. *)
(* 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.
(** *** Alternative notation *)
(** An equivalent definition with a [rec-fix] notation looking like [fix], where the recursive call can be given a more specific name. *)
Definition factorial' : nat -> itree E nat :=
rec-fix fact x :=
match x with
| 0 => Ret 1
| S m => y <- fact m ;; Ret (x * y)
end.

(** These two definitions are definitionally equal. *)
Lemma factorial_same : factorial = factorial'.