Library Io.Exception

Require Import Coq.Lists.List.
Require Import FunctionNinjas.All.
Require Import Io.All.
Require Io.List.

Import ListNotations.
Import C.Notations.

Module Command.
  Inductive t (E : Effect.t) (Exc : Type) :=
  | Ok (command : Effect.command E)
  | Exc (exc : Exc).
  Arguments Ok [E Exc] _.
  Arguments Exc [E Exc] _.
End Command.

Definition answer {E : Effect.t} {Exc : Type} (c : Command.t E Exc) : Type :=
  match c with
  | Command.Ok cEffect.answer E c
  | Command.Exc excEmpty_set
  end.

Definition effect (E : Effect.t) (Exc : Type) : Effect.t :=
  Effect.New (Command.t E Exc) answer.

Definition lift {E : Effect.t} {Exc A : Type} (x : C.t E A)
  : C.t (effect E Exc) A :=
  C.run (fun cC.Call (effect E Exc) (Command.Ok c)) x.

Definition raise {E : Effect.t} {Exc A : Type} (exc : Exc)
  : C.t (effect E Exc) A :=
  let! absurd := C.Call (effect E Exc) (Command.Exc exc) in
  match absurd with end.

Fixpoint run {E : Effect.t} {Exc A : Type} (x : C.t (effect E Exc) A)
  : C.t E (A + list Exc) :=
  match x with
  | C.Ret _ xret @@ inl x
  | C.Call (Command.Ok c) ⇒
    let! answer := C.Call E c in
    ret @@ inl answer
  | C.Call (Command.Exc exc) ⇒ ret @@ inr [exc]
  | C.Let _ _ x f
    let! x := run x in
    match x with
    | inl xrun (f x)
    | inr excret @@ inr exc
    end
  | C.Join _ _ x y
    let! xy := join (run x) (run y) in
    match xy with
    | (inl x, inl y)ret @@ inl (x, y)
    | (inr exc, inl _) | (inl _, inr exc)ret @@ inr exc
    | (inr exc_x, inr exc_y)ret @@ inr (exc_x ++ exc_y)
    end
  | C.First _ _ x y
    let! xy := first (run x) (run y) in
    match xy with
    | inl (inl x) ⇒ ret @@ inl @@ inl x
    | inr (inl y) ⇒ ret @@ inl @@ inr y
    | inl (inr exc) | inr (inr exc) ⇒ ret @@ inr exc
    end
  end.

Definition handle {E : Effect.t} {Exc : Type} (run_exc : ExcC.t E unit)
  (x : C.t E (unit + list Exc)) : C.t E unit :=
  let! x := x in
  match x with
  | inl xret x
  | inr excIo.List.iter_seq run_exc exc
  end.