module lib.Maybe where

open import Level renaming (zero to ze ; suc to su)
open import Data.Empty
open import Data.Unit hiding (_≟_)
open import Data.Bool hiding (_≟_)
open import Data.Product hiding (map)
open import Function

returnType bindType mapType appType :  {} (M : Set   Set )  Set (su )
returnType {} M =  {A : Set } (a : A)  M A
bindType   {} M =  {A B : Set } (t : M A) (ρ : A  M B)  M B
mapType    {} M =  {A B : Set } (f : A  B) (ma : M A)  M B
appType    {} M =  {A : Set } (ma₁ ma₂ : M A)  M A

eqType :  {}  Set   Set 
eqType A = A  A  Bool

private

  module Dummy where

    data Maybe {} (A : Set ) : Set  where
      none :           Maybe A
      some : (a : A)  Maybe A

    syntax Maybe A = A ??

open Dummy public hiding (module Maybe)

maybe :  { ℓ′} {A : Set } {B : Set ℓ′} (s : A  B) (n : B) (ma : Maybe A)  B
maybe s n none     = n
maybe s n (some a) = s a


module Maybe where

  isSome :   {} {A : Set } (ma : Maybe A)  Bool
  isSome = maybe (const true) false

  fromSome :  {} {A : Set } (ma : Maybe A) {prf : maybe (const )  ma}  A
  fromSome none     = λ {}
  fromSome (some a) = a

  isNone :   {} {A : Set } (ma : Maybe A)  Bool
  isNone = not  isSome

  induction :  {} {A : Set } (P : Maybe A  Set)
              (Pn : P none) (Pa : (a : A)  P $ some a) (ma : Maybe A)  P ma
  induction P Pn Pa none     = Pn
  induction P Pn Pa (some a) = Pa a

  map :  {}  mapType {} Maybe
  map f = maybe (some  f) none

  bind :  {}  bindType {} Maybe
  bind ma f = maybe f none ma

  return :  {}  returnType {} Maybe
  return = some

  app :  {}  appType {} Maybe
  app none     ma₂ = ma₂
  app (some a) ma₂ = some a

  infix -10 do_
  infixr -5 doBind doMap appMap
  do_ :  {} {A : Set } (ma : Maybe A)  Maybe A
  do_ = id

  doBind :  {}  bindType {} Maybe
  doBind = bind

  appMap doMap :   {}  mapType {} Maybe
  appMap  = map
  doMap   = map

  syntax appMap f ma         = f <$> ma
  syntax bind ma f           = ma >>= f
  syntax doBind ma  x  f) = x ← ma , f
  syntax doMap  x  f) ma  = x ←′ ma , f

module Predicates where

  data isSome {} {A : Set } : (ma : Maybe A)  Set  where
    some : (a : A)  isSome (some a)

  fromSome :  {} {A : Set } {ma : Maybe A} (pr : isSome ma)  A
  fromSome (some a) = a

  bind :  {} {A B : Set } {ma : Maybe A} (pr : isSome ma) {f : A  Maybe B} 
         isSome (f $ fromSome pr)  isSome (Maybe.bind ma f)
  bind (some a) pr = pr