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

open import Relation.Unary
open import Function
open import Data.Product 

open import Core.Functor
open import Core.Functor.NaturalTransformation
open import Core.Functor.Monad

open import Core.Signature
open import Core.Extensionality

open import Relation.Binary.PropositionalEquality using (refl ; _≡_ ; subst ; sym ; trans ; cong)

module Effect.Syntax.Hefty where

data Hefty (σ : Signature) (A : Set) : Set where
  pure   : A                   Hefty σ A 
  impure :  σ  (Hefty σ) A   Hefty σ A

variable m m₁ m₂ m₃ m′ : Hefty σ A 

instance hefty-pointed : Pointed (Hefty σ)
hefty-pointed = record
  { point = pure
  } 

fold-hefty : ∀[ id  F ]  Algebra σ F  ∀[ Hefty σ  F ]
fold-hefty η y (pure x)                = η x
fold-hefty η y (impure  c , r , s  ) = y .α  c , fold-hefty η y  r , fold-hefty η y  s  

rec-hefty :  Pointed F   (A  F B)  Algebra σ F  Hefty σ A  F B
rec-hefty k _ (pure x)               = k x
rec-hefty k y (impure  c , r , s ) = y .α  c , rec-hefty k y  r , rec-hefty point y  s 

map-hefty : (A  B)  Hefty σ A  Hefty σ B
map-hefty f (pure x) = pure (f x)
map-hefty f (impure  c , r , s ) = impure  c , map-hefty f  r , s 

map-hefty-id : (t : Hefty σ A)  map-hefty id t  t 
map-hefty-id (pure _)               = refl
map-hefty-id (impure  c , r , s ) =
  cong    impure  c ,  , s )
    (extensionality (map-hefty-id  r ))

map-hefty-∘ :
   {C : Set}  (f : A  B) (g : B  C) (t : Hefty σ A)
   map-hefty g (map-hefty f t)  map-hefty (g  f) t
map-hefty-∘ f g (pure _)              = refl
map-hefty-∘ f g (impure  c , r , s ) =
  cong    impure  c ,  , s )
    (extensionality (map-hefty-∘ f g  r))

bind-hefty : Hefty σ A  (A  Hefty σ B)  Hefty σ B
bind-hefty t k = rec-hefty k  where .α  impure) t

instance
  hefty-functor : Functor (Hefty σ)
  hefty-functor = record
    { fmap    = map-hefty
    ; fmap-id = extensionality map-hefty-id
    ; fmap-∘  = λ f g  extensionality λ m  sym (map-hefty-∘ f g m)
    }


  hefty-monad : Monad (Hefty σ)
  hefty-monad = record
    { return         = point
    ; _∗             = flip bind-hefty
    ; >>=-idˡ        = λ _ _  refl
    ; >>=-idʳ        = right-identity
    ; >>=-assoc      = assoc
    ; return-natural = λ where .commute _  refl
    }
    where 
      open import Relation.Binary.PropositionalEquality

      right-identity : (m : Hefty σ A)  flip bind-hefty pure m  m
      right-identity {σ} {A} (pure x)               = refl
      right-identity {σ} {A} (impure  c , r , s ) = 
        cong₂  ○₁ ○₂  impure  c , ○₁ , ○₂ )
          (extensionality $ right-identity  r)
          (extensionality $ right-identity  s) 
 
      assoc : {A B D : Set} (k₁ : A  Hefty σ B) (k₂ : B  Hefty σ D) (m : Hefty σ A)
             flip  y x  bind-hefty x y) (flip  y x  bind-hefty x y) m k₁) k₂
             flip  y x  bind-hefty x y) m  x  flip  y x₁  bind-hefty x₁ y) (k₁ x) k₂)
      assoc k₁ k₂ (pure x) = refl
      assoc k₁ k₂ (impure  c , r , s ) =
        cong₂  ○₁ ○₂  impure  c , ○₁ , ○₂ )
          (extensionality $ assoc k₁ k₂  r)
          (extensionality $ assoc pure pure  s)