Library ASCommon.CResult


From stdpp Require Import option.
Require Import Options.
Require Import CBase CDestruct CMonads.

The point of this module is to keep the sum type symmetric and use this to assign a meaning to success and error (and a corresponding monad instance)
The naming is intended to match Ocaml's result as Haskell uses Either which is similar to the regular sum.

Section Result.

The error type is first so that result E is a monad
  Context {E A : Type}.

  Inductive result : Type :=
  | Ok (val : A)
  | Error (err : E).

  Definition get_Ok (r : result) : option A :=
    match r with
    | Ok valSome val
    | Error errNone
    end.

  Definition get_Error (r : result) : option E :=
    match r with
    | Ok valNone
    | Error errSome err
    end.

Takes an option but convert None into the provided error
  Definition res_from_opt (e : E) : option A result :=
  from_option Ok (Error e).

  Definition res_to_opt := get_Ok.

  Definition res_from_suml (s : A + E) : result :=
    match s with
    | inl valOk val
    | inr errError err
    end.

  Definition res_from_sumr (s : E + A) : result :=
    match s with
    | inr valOk val
    | inl errError err
    end.

  Definition res_to_suml (r : result) : A + E :=
    match r with
    | Ok valinl val
    | Error errinr err
    end.

  Definition res_to_sumr (r : result) : E + A :=
    match r with
    | Ok valinr val
    | Error errinl err
    end.

  Lemma res_from_to_suml (s : A + E) : res_to_suml (res_from_suml s) = s.
  Proof using. by destruct s. Qed.

  Lemma res_to_from_suml (r : result) : res_from_suml (res_to_suml r) = r.
  Proof using. by destruct r. Qed.

  Lemma res_from_to_sumr (s : E + A) : res_to_sumr (res_from_sumr s) = s.
  Proof using. by destruct s. Qed.

  Lemma res_to_from_sumr (r : result) : res_from_sumr (res_to_sumr r) = r.
  Proof using. by destruct r. Qed.

is_Err and is_Ret are doing the same as is_Some but for results
  Definition is_Error (r : result) := err, r = Error err.
  #[export] Instance is_Error_Decision (r : result) : Decision (is_Error r).
  Proof.
    refine (match r with | Error _left _ | _right _ end);
      unfold is_Error;
      naive_solver.
  Defined.

  Definition is_Ok (r : result) := val, r = Ok val.
  #[export] Instance is_Ok_Decision (r : result) : Decision (is_Ok r).
  Proof.
    refine (match r with | Ok _left _ | _right _ end);
      unfold is_Ok;
      naive_solver.
  Defined.

  #[export] Instance cdestruct_is_Ok r :
    CDestrSimpl false (is_Ok r) ( o, r = Ok o).
  Proof. tcclean. now unfold is_Ok. Qed.

  #[export] Instance obv_true_is_Ok_Ok x : ObvTrue (is_Ok (Ok x)).
  Proof. tcclean. unfold is_Ok. eauto. Qed.

  #[export] Instance obv_false_is_Ok_Error x : ObvFalse (is_Ok (Error x)).
  Proof. tcclean. unfold is_Ok. naive_solver. Qed.

Unpack a result into any monad that supports that error type
  Definition unpack_result `{MThrow E M, MRet M} (r : result) : M A :=
    match r with
    | Ok valmret val
    | Error errmthrow err
    end.

  #[export] Instance result_eq_dec `{EqDecision E} `{EqDecision A} : EqDecision (result).
  Proof. solve_decision. Defined.

End Result.
Arguments result : clear implicits.

Instance DecisionT_result `{DecisionT E} `{DecisionT A} : DecisionT (result E A).
Proof. sfirstorder. Qed.

Instance result_inhabited_ok {E} `{Inhabited A} : Inhabited (result E A).
Proof. firstorder. Defined.

Instance result_inhabited_error `{Inhabited E} {A} : Inhabited (result E A).
Proof. firstorder. Defined.

CDestruct interaction


Definition cdestruct_result (E A : Type) :
  CDestrCase (result E A) := ltac:(constructor).

Result as monad

Section ResultMonad.
  Context {E : Type}.

  #[export] Instance result_ret : MRet (result E) := @Ok E.

  #[export] Instance result_throw : MThrow E (result E) := @Error E.

  #[export] Instance result_bind : MBind (result E) :=
    λ _ _ f r,
      match r with
      | Ok valf val
      | Error errError err
      end.

  #[export] Instance result_join : MJoin (result E) :=
    λ _ r,
      match r with
      | Error errError err
      | Ok (Error err) ⇒ Error err
      | Ok (Ok a) ⇒ Ok a
      end.

  #[export] Instance result_fmap : FMap (result E) :=
    λ _ _ f r,
      match r with
      | Ok valOk (f val)
      | Error errError err
      end.

  #[export] Instance result_monad : Monad (result E).
  Proof. split; cdestruct |- *** ## cdestruct_result. Qed.

  #[export] Instance result_monad_fmap : MonadFMap (result E).
  Proof. cdestruct |- *** ## cdestruct_result. Qed.

End ResultMonad.

Error map


Definition mapE {E E' A} (f : E E') (r : result E A) : result E' A :=
  match r with
  | Ok valOk val
  | Error errError (f err)
  end.