PLFA agda exercises
------------------------------------------------------------------------
-- The Agda standard library
--
-- Monads
------------------------------------------------------------------------

-- Note that currently the monad laws are not included here.

{-# OPTIONS --cubical-compatible --safe #-}

module Effect.Monad where

open import Data.Bool.Base using (Bool; true; false; not)
open import Data.Unit.Polymorphic.Base using (⊤)

open import Effect.Choice
open import Effect.Empty
open import Effect.Applicative
open import Function.Base using (id; flip; _$′_; _∘′_)
open import Level using (Level; suc; _⊔_)

private
  variable
    f g g₁ g₂ : Level
    A B C : Set f

------------------------------------------------------------------------
-- The type of raw monads

record RawMonad (F : Set f → Set g) : Set (suc f ⊔ g) where
  infixl 1 _>>=_ _>>_ _>=>_
  infixr 1 _=<<_ _<=<_
  field
    rawApplicative : RawApplicative F
    _>>=_ : F A → (A → F B) → F B

  open RawApplicative rawApplicative public

  _>>_ : F A → F B → F B
  _>>_ = _*>_

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

  Kleisli : Set f → Set f → Set (f ⊔ g)
  Kleisli A B = A → F B

  _>=>_ : Kleisli A B → Kleisli B C → Kleisli A C
  (f >=> g) a = f a >>= g

  _<=<_ : Kleisli B C → Kleisli A B → Kleisli A C
  _<=<_ = flip _>=>_

  when : Bool → F ⊤ → F ⊤
  when true m = m
  when false m = pure _

  unless : Bool → F ⊤ → F ⊤
  unless = when ∘′ not

-- When level g=f, a join/μ operator is definable

module Join {F : Set f → Set f} (M : RawMonad F) where
  open RawMonad M

  join : F (F A) → F A
  join = _>>= id

-- Smart constructor

module _ where

  open RawMonad
  open RawApplicative

  mkRawMonad :
    (F : Set f → Set g) →
    (pure : ∀ {A} → A → F A) →
    (bind : ∀ {A B} → F A → (A → F B) → F B) →
    RawMonad F
  mkRawMonad F pure _>>=_ .rawApplicative =
    mkRawApplicative _ pure $′ λ mf mx → do
      f ← mf
      x ← mx
      pure (f x)
  mkRawMonad F pure _>>=_ ._>>=_ = _>>=_

------------------------------------------------------------------------
-- The type of raw monads with a zero

record RawMonadZero (F : Set f → Set g) : Set (suc f ⊔ g) where
  field
    rawMonad : RawMonad F
    rawEmpty : RawEmpty F

  open RawMonad rawMonad public
  open RawEmpty rawEmpty public

  rawApplicativeZero : RawApplicativeZero F
  rawApplicativeZero = record
    { rawApplicative = rawApplicative
    ; rawEmpty = rawEmpty
    }

------------------------------------------------------------------------
-- The type of raw monadplus

record RawMonadPlus (F : Set f → Set g) : Set (suc f ⊔ g) where
  field
    rawMonadZero : RawMonadZero F
    rawChoice    : RawChoice F

  open RawMonadZero rawMonadZero public
  open RawChoice rawChoice public

  rawAlternative : RawAlternative F
  rawAlternative = record
    { rawApplicativeZero = rawApplicativeZero
    ; rawChoice = rawChoice
    }

------------------------------------------------------------------------
-- The type of raw monad transformer

-- F has been RawMonadT'd as TF
record RawMonadTd (F : Set f → Set g₁) (TF : Set f → Set g₂) : Set (suc f ⊔ g₁ ⊔ g₂) where
  field
    lift : F A → TF A
    rawMonad : RawMonad TF

  open RawMonad rawMonad public

RawMonadT : (T : (Set f → Set g₁) → (Set f → Set g₂)) → Set (suc f ⊔ suc g₁ ⊔ g₂)
RawMonadT T = ∀ {M} → RawMonad M → RawMonadTd M (T M)