module Stdlib.Data.Result;

import Stdlib.Data.Result.Base open public;
import Stdlib.Data.Bool.Base open;

import Stdlib.Trait.Eq open;
import Stdlib.Trait.Ord open;
import Stdlib.Trait open;

{-# specialize: true, inline: case #-}
instance
ordResultI {A B} {{Ord A}} {{Ord B}} : Ord (Result A B) :=
  mkOrd@{
    cmp : Result A B -> Result A B -> Ordering
      | (error a1) (error a2) := Ord.cmp a1 a2
      | (ok b1) (ok b2) := Ord.cmp b1 b2
      | (error _) (ok _) := LT
      | (ok _) (error _) := GT
  };

{-# specialize: true, inline: case #-}
instance
eqResultI {A B} {{Eq A}} {{Eq B}} : Eq (Result A B) :=
  mkEq@{
    eq : Result A B -> Result A B -> Bool
      | (error a1) (error a2) := a1 == a2
      | (ok b1) (ok b2) := b1 == b2
      | _ _ := false
  };

{-# specialize: true, inline: case #-}
instance
functorResultI {err} : Functor (Result err) :=
  mkFunctor@{
    map := mapOk
  };

{-# specialize: true, inline: true #-}
instance
monomorphicFunctorResultI
  {err res} : Monomorphic.Functor (Result err res) res :=
  fromPolymorphicFunctor;

instance
applicativeResultI {err} : Applicative (Result err) :=
  mkApplicative@{
    pure := ok;
    ap {A B} : Result err (A -> B) -> Result err A -> Result err B
      | (ok f) (ok x) := ok (f x)
      | (ok _) (error e) := error e
      | (error e) _ := error e
  };

instance
monadResultI {err} : Monad (Result err) :=
  mkMonad@{
    bind {A B} : Result err A -> (A -> Result err B) -> Result err B
      | (error e) _ := error e
      | (ok a) f := f a
  };