/-
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.category.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.
-/
meta inductive exceptional (α : Type)
| success   : α → exceptional
| exception : (options → format) → exceptional

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

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

meta instance : has_to_string (exceptional α) :=
has_to_string.mk exceptional.to_string
end

namespace exceptional
variables {α β : Type}

protected meta def to_bool : exceptional α → bool
| (success _)      := tt
| (exception .α _) := ff

protected meta def to_option : exceptional α → option α
| (success a)      := some a
| (exception .α _) := none

@[inline] protected meta def fmap (f : α → β) (e : exceptional α) : exceptional β :=
exceptional.cases_on e
  (λ a, success (f a))
  (λ f, exception β f)

@[inline] protected meta def bind (e₁ : exceptional α) (e₂ : α → exceptional β) : exceptional β :=
exceptional.cases_on e₁
  (λ a, e₂ a)
  (λ f, exception β f)

@[inline] protected meta def return (a : α) : exceptional α :=
success a

@[inline] meta def fail (f : format) : exceptional α :=
exception α (λ u, f)
end exceptional

meta instance : monad exceptional :=
{map := @exceptional.fmap, ret := @exceptional.return, bind := @exceptional.bind}
