------------------------------------------------------------------------
-- The Agda standard library
--
-- Universe-sensitive functor and monad instances for the Product type.
------------------------------------------------------------------------

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

open import Algebra

module Data.Product.Effectful.Examples
  {a e b} {A : Monoid a e} {B : Set b} where

open import Level using (Lift; lift; _⊔_)
open import Effect.Functor using (RawFunctor)
open import Effect.Monad using (RawMonad)
open import Data.Product.Base using (_×_; _,_)
open import Data.Product.Relation.Binary.Pointwise.NonDependent
open import Function.Base using (id)
import Function.Identity.Effectful as Id
open import Relation.Binary.Core using (Rel)
open import Relation.Binary.PropositionalEquality.Core using (_≡_; refl)

------------------------------------------------------------------------
-- Examples

-- Note that these examples are simple unit tests, because the type
-- checker verifies them.

private

  module A = Monoid A

  open import Data.Product.Effectful.Left A.rawMonoid b

  _≈_ : Rel (A.Carrier × Lift a B) (e  a  b)
  _≈_ = Pointwise A._≈_ _≡_

  open RawFunctor functor

  -- This type to the right of × needs to be a "lifted" version of
  -- (B : Set b) that lives in the universe (Set (a ⊔ b)).
  fmapIdₗ : (x : A.Carrier × Lift a B)  (id <$> x)  x
  fmapIdₗ x = A.refl , refl

  open RawMonad monad

  -- Now, let's show that "pure" is a unit for >>=. We use Lift in
  -- exactly the same way as above. The data (x : B) then needs to be
  -- "lifted" to this new type (Lift B).
  pureUnitL :  {x : B} {f : Lift a B  A.Carrier × Lift a B} 
                (pure (lift x) >>= f)  f (lift x)
  pureUnitL = A.identityˡ _ , refl

  pureUnitR : {x : A.Carrier × Lift a B}  (x >>= pure)  x
  pureUnitR = A.identityʳ _ , refl

  -- And another (limited version of a) monad law...
  bindCompose :  {f g : Lift a B  A.Carrier × Lift a B} 
                {x : A.Carrier × Lift a B} 
                ((x >>= f) >>= g)  (x >>=  y  (f y >>= g)))
  bindCompose = A.assoc _ _ _ , refl