/-
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import init.meta.interactive init.category.transformers

universes u v

def option_t (m : Type u → Type v) [monad m] (α : Type u) : Type v :=
m (option α)

@[inline] def option_t_bind {m : Type u → Type v} [monad m] {α β : Type u} (a : option_t m α) (b : α → option_t m β)
                               : option_t m β :=
show m (option β), from
do o ← a,
   match o with
   | none     := return none
   | (some a) := b a
   end

@[inline] def option_t_return {m : Type u → Type v} [monad m] {α : Type u} (a : α) : option_t m α :=
show m (option α), from
return (some a)

instance {m : Type u → Type v} [monad m] : monad (option_t m) :=
{pure := @option_t_return m _, bind := @option_t_bind m _,
 id_map := begin
   intros,
   simp [option_t_bind, function.comp],
   have h : option_t_bind._match_1 option_t_return = @pure m _ (option α),
   { apply funext, intro s, cases s, refl, refl },
   { simp [h], apply @monad.bind_pure _ (option α) m },
 end,
 pure_bind := begin
   intros,
   simp [option_t_bind, option_t_return, monad.pure_bind]
 end,
 bind_assoc :=
 begin
   intros,
   simp [option_t_bind, option_t_return, monad.bind_assoc],
   apply congr_arg, apply funext, intro x,
   cases x,
   { simp [option_t_bind, monad.pure_bind] },
   { refl }
 end}

def option_t_orelse {m : Type u → Type v} [monad m] {α : Type u} (a : option_t m α) (b : option_t m α) : option_t m α :=
show m (option α), from
do o ← a,
   match o with
   | none     := b
   | (some v) := return (some v)
   end

def option_t_fail {m : Type u → Type v} [monad m] {α : Type u} : option_t m α :=
show m (option α), from
return none

instance {m : Type u → Type v} [monad m] : alternative (option_t m) :=
{ @option_t.monad m _ with
  failure := @option_t_fail m _,
  orelse  := @option_t_orelse m _ }

def option_t.lift {m : Type u → Type v} [monad m] {α : Type u} (a : m α) : option_t m α :=
(some <$> a : m (option α))

instance option_t.monad_transformer : monad.monad_transformer option_t :=
{ is_monad   := @option_t.monad,
  monad_lift := @option_t.lift }
