/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import init.monad init.meta.format
/-
Remark: we use a function that produces a format object as the exception information.
Motivation: the formatting object may be big, and we may create it on demand.
-/
inductive exceptional (A : Type) :=
| success   : A → exceptional A
| exception : (options → format) → exceptional A

section
open exceptional
variables {A : Type}
variables [has_to_string A]

protected meta_definition exceptional.to_string : exceptional A → string
| (success a)     := to_string a
| (exception A e) := "Exception: " + to_string (e options.mk)

protected meta_definition exceptional.has_to_string [instance] : has_to_string (exceptional A) :=
has_to_string.mk exceptional.to_string
end

namespace exceptional
variables {A B : Type}

inline protected meta_definition fmap (f : A → B) (e : exceptional A) : exceptional B :=
exceptional.cases_on e
  (λ a, success (f a))
  (λ f, !exception f)

inline protected meta_definition bind (e₁ : exceptional A) (e₂ : A → exceptional B) : exceptional B :=
exceptional.cases_on e₁
  (λ a, e₂ a)
  (λ f, !exception f)

inline protected meta_definition return (a : A) : exceptional A :=
success a

inline meta_definition fail (f : format) : exceptional A :=
!exception (λ u, f)
end exceptional

meta_definition exceptional.is_monad [instance] : monad exceptional :=
monad.mk @exceptional.fmap @exceptional.return @exceptional.bind
