{-# OPTIONS --without-K --safe #-}

module Prelude.Monad where

open import Agda.Builtin.Bool

open import Prelude.Level
open import Prelude.Function

open import Prelude.Functor
open import Prelude.Applicative
open import Prelude.List

private variable
  A B : Set _

record Monad (M : ∀ {ℓ} → Set ℓ → Set ℓ) : Setω where
  infixr 0 caseM_of_
  infixr 1 _=<<_
  infixl 1 _>>=_ _>>_
  field
    _>>=_ : M A → (A → M B) → M B
    overlap ⦃ super ⦄ : Applicative M

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

  _=<<_ : (A → M B) → M A → M B
  _=<<_ = flip _>>=_

  return = pure
  join : {A : Set ℓ} → M (M A) → M A
  join = _=<<_ id

  caseM_of_ = _>>=_

  ifM_then_else_ : M Bool → M A → M A → M A
  ifM mb then mx else my = caseM mb of λ where
    true  → mx
    false → my

  mapM : (A → M B) → List A → M (List B)
  mapM f []       = return []
  mapM f (x ∷ xs) = ⦇ f x ∷ mapM f xs ⦈

  forM : List A → (A → M B) → M (List B)
  forM = flip mapM

open Monad ⦃...⦄ public

{-# DISPLAY Monad._>>=_  _ = _>>=_  #-}
{-# DISPLAY Monad._=<<_  _ = _=<<_  #-}
{-# DISPLAY Monad._>>_   _ = _>>_   #-}

monadAp : {M : ∀ {ℓ} → Set ℓ → Set ℓ} ⦃ _ : Functor M ⦄
  → (M (A → B) → ((A → B) → M B) → M B)
  → M (A → B) → M A → M B
monadAp _>>=_ mf mx = mf >>= λ f → fmap f mx