Source code on Github
{-# OPTIONS --safe #-}
module Prelude.Monad where

open import Prelude.Init; open SetAsType
open import Prelude.Functor
open import Prelude.Applicative
open import Prelude.General

{-
Monad : (Type ℓ → Type ℓ) → Type (lsuc ℓ)
Monad {ℓ = ℓ} = RawMonad {f = ℓ}
open RawMonad ⦃...⦄ public
  using (return; _>>=_; _>>_; _=<<_; _>=>_; _<=<_; join)
-}

private variable A : Type ; B : Type ℓ′; C : Type ℓ″; M : Type↑

record Monad (M : Type↑) : Typeω where
  infixl 1 _>>=_ _>>_ _≫=_ _≫_ _>=>_
  infixr 1 _=<<_ _=≪_ _<=<_

  field
    overlap  super  : Applicative M
    return : A  M A
    _>>=_  : M A  (A  M B)  M B

  _>>_ : M A  M B  M B
  m₁ >> m₂ = m₁ >>= λ _  m₂

  _=<<_ : (A  M B)  M A  M B
  f =<< c = c >>= f

  _≫=_ = _>>=_; _≫_  = _>>_; _=≪_ = _=<<_

  _>=>_ : (A  M B)  (B  M C)  (A  M C)
  f >=> g = _=<<_ g  f

  _<=<_ : (B  M C)  (A  M B)  (A  M C)
  g <=< f = f >=> g

  join : M (M A)  M A
  join m = m >>= id
open Monad ⦃...⦄ public

record MonadLaws (M : Type↑)  _ : Monad M  : Typeω where
  field
    >>=-identityˡ :  {A : Type } {B : Type ℓ′} {a : A} {h : A  M B} 
      (return a >>= h)  h a
    >>=-identityʳ :  {A : Type } (m : M A) 
      (m >>= return)  m
    >>=-assoc :  {A : Type } {B : Type ℓ′} {C : Type ℓ″} (m : M A) {g : A  M B} {h : B  M C} 
      ((m >>= g) >>= h)  (m >>=  x  g x >>= h))
open MonadLaws ⦃...⦄ public

record Lawful-Monad (M : Type↑) : Typeω where
  field  isMonad  : Monad M
         hasMonadLaws  : MonadLaws M
open Lawful-Monad ⦃...⦄ using () public
instance
  mkLawful-Monad :  _ : Monad M    MonadLaws M   Lawful-Monad M
  mkLawful-Monad = record {}

record Monad′ (M : Type[   ℓ′ ]) : Type (lsuc  ⊔ₗ ℓ′) where
  infixl 1 _>>=′_ _>>′_ _≫=′_ _≫′_ _>=>′_
  infixr 1 _=<<′_ _=≪′_ _<=<′_

  field
    return′ : A  M A
    _>>=′_  : M A  (A  M B)  M B

  _>>′_ : M A  M B  M B
  m₁ >>′ m₂ = m₁ >>=′ λ _  m₂

  _=<<′_ : (A  M B)  M A  M B
  f =<<′ c = c >>=′ f

  _>=>′_ : (A  M B)  (B  M C)  (A  M C)
  f >=>′ g = _=<<′_ g  f

  _<=<′_ : (B  M C)  (A  M B)  (A  M C)
  g <=<′ f = f >=>′ g

  _≫=′_ = _>>=′_; _≫′_ = _>>′_; _=≪′_ = _=<<′_

  -- join : M (M A) → M A
  -- join m = m >>= id

open Monad′ ⦃...⦄ public

record Monad₀ (M : Type↑) : Typeω where
  field  isMonad  : Monad M
         isApplicative₀  : Applicative₀ M
open Monad₀ ⦃...⦄ using () public
instance
  mkMonad₀ :  Monad M    Applicative₀ M   Monad₀ M
  mkMonad₀ = record {}

record Monad⁺ (M : Type↑) : Typeω where
  field  isMonad  : Monad M
         isAlternative  : Alternative M
open Monad⁺ ⦃...⦄ using () public
instance
  mkMonad⁺ :  Monad M    Alternative M   Monad⁺ M
  mkMonad⁺ = record {}

instance
  Monad-Maybe : Monad Maybe
  Monad-Maybe = λ where
    .return  pure
    ._>>=_ m f  maybe f nothing m

  MonadLaws-Maybe : MonadLaws Maybe
  MonadLaws-Maybe = λ where
    .>>=-identityˡ  refl
    .>>=-identityʳ  λ where
      (just _)  refl
      nothing   refl
    .>>=-assoc  λ where
      (just _)  refl
      nothing   refl

  Monad-List : Monad List
  Monad-List = λ where
    .return  pure
    ._>>=_  flip concatMap

  Monad-TC : Monad Meta.TC
  Monad-TC = record {R}
    where import Reflection as R using (return) renaming (bindTC to _>>=_)

{- ** Id monad: provides us with forward composition as _>=>_,
                but breaks instance-resolution/typeclass-inference
module IdMonad where
  Id : Type ℓ → Type ℓ
  Id = id

  Monad-Id : Monad Id
  Monad-Id .return = id
  Monad-Id ._>>=_ = _|>_
-}

-- ** monadic utilities
module _  _ : Monad M  where
  mapM : (A  M B)  List A  M (List B)
  mapM f []       = return []
  mapM f (x  xs) =  f x  mapM f xs 

  concatMapM : (A  M (List B))  List A  M (List B)
  concatMapM f xs = concat <$> mapM f xs

  forM : List A  (A  M B)  M (List B)
  forM []       _ = return []
  forM (x  xs) f =  f x  forM xs f 

  concatForM : List A  (A  M (List B))  M (List B)
  concatForM xs f = concat <$> forM xs f

  return⊤ void : M A  M 
  return⊤ k = k  return tt
  void = return⊤

  filterM : (A  M Bool)  List A  M (List A)
  filterM _ [] = return []
  filterM p (x  xs) = do
    b  p x
    ((if b then [ x ] else []) ++_) <$> filterM p xs

  -- traverse : ∀ {A B : Type} {M : Type → Type} → ⦃ Applicative M ⦄ → ⦃ Monad M ⦄ → (A → M B) → List A → M (List B)
  -- traverse f = λ where
  --   [] → return []
  --   (x ∷ xs) → ⦇ f x ∷ traverse f xs ⦈

do-pure :  {A : Type } {x : A} {mx : Maybe A} {f : A  Bool}
   mx  just x
   f x  true
   M.fromMaybe false (mx >>= pure  f)  true
do-pure refl f≡ rewrite f≡ = refl

private
  _ : (return 5 >>= just)  just 5
  _ = refl
  _ : (return 5 >>= just)  just 5
  _ = >>=-identityʳ _

  _ :  _ : Lawful-Monad M   (  M )
  _ = return

  _ : Lawful-Monad Maybe
  _ = itω