{-# OPTIONS --safe #-}

open import Signatures
open import Prelude

module FreeMonad.Syntax.AsSignature (𝔽 : Signature) where

open import FreeMonad.Syntax 𝔽
open Signature 𝔽

Position : Syntax A  Type
Position (var _)        = 
Position (op (Oᵢ , k))  = Σ[ i  Arity Oᵢ ] × Position (k i)

 : Signature
 = Syntax   Position

open SyntaxBind using (_<$>_; _>>=_)

shape : Syntax A  Syntax 
shape t = const tt <$> t

Index : Syntax A  Type
Index = Position  shape

lookup : (s : Syntax A)  Index s  A
lookup (var x)       _         = x
lookup (op (O , k))  (i , is)  = lookup (k i) is

fill : (s : Syntax A)  (Position s  B)  Syntax B
fill (var x)       i = var (i tt)
fill (op (O , k))  i = op (O , λ j  fill (k j) (i  _,_ j))

⟦op⟧ :  𝔽  (   A)     A
⟦op⟧ (S , P) = op (S , fst  P) , uncurry (snd ∘′ P)

Indexed : Syntax A     A
Indexed .fun s = shape s , lookup s
Indexed .inv = uncurry fill
Indexed .leftInv =
  elim-s _ λ { (var x)  refl ; (op (O , k) P⟨xs⟩)  cong (op  _,_ O) (funExt P⟨xs⟩) }
Indexed .rightInv = uncurry (elim-s _ λ { (var x) P  refl ; (op (O , k) P⟨xs⟩) P  cong (⟦op⟧  _,_ O) (funExt λ i  P⟨xs⟩ i _) })

<$>⇔cmap : (f : A  B) (s : Syntax A)  f <$> s  Indexed .inv (cmap f (Indexed .fun s))
<$>⇔cmap f = elim-s _ λ { (var x)  refl ; (op (O , k) P⟨xs⟩)  cong (op  _,_ O) (funExt P⟨xs⟩) }

<$>-com-inv : (f : A  B) (s :    A)  f <$> Indexed .inv s  Indexed .inv (cmap f s)
<$>-com-inv f s = <$>⇔cmap f (Indexed .inv s) ; cong (Indexed .inv  cmap f) (Indexed .rightInv s)

module _ (f : A  B) (s : Syntax A) where
  <$>-com-fun : cmap f (Indexed .fun s)  Indexed .fun (f <$> s)
  <$>-com-fun = sym (cong (Indexed .fun) (<$>⇔cmap f s) ; Indexed .rightInv (cmap f (Indexed .fun s)))

  <$>-same-shape :  shape s  shape (f <$> s)
  <$>-same-shape = PathPΣ <$>-com-fun .fst

  <$>-lookup :  i  lookup (f <$> s) i  f (lookup s (subst Position (sym <$>-same-shape) i))
  <$>-lookup i = sym (cong (_$ i) (fromPathP (PathPΣ <$>-com-fun .snd))) ; transportRefl _

open import Isomorphism.Properties

infixr 5 _∈_ _∉_
_∈_ : A  Syntax A  Type _
x  xs = Fibre (lookup xs) x

_∉_ : A  Syntax A  Type _
x  xs = ¬ (x  xs)

open import Data.Sigma.Properties

module _ (disc :  Oᵢ  Discrete (Signature.Arity 𝔽 Oᵢ)) where
  disc-index : (s : Syntax A)  Discrete (Index s)
  disc-index =
    elim-s _ λ { (var v) _ _  yes refl
               ; (op (O , k) P⟨xs⟩)  discreteΣ (disc O) P⟨xs⟩ }

open SyntaxBind

>>=-ind : Syntax A  (A  Syntax B)  Type
>>=-ind xs k = Index (xs >>= k)

>>=∙-ind : Syntax A  (A  Syntax B)  Type
>>=∙-ind xs k = Σ[ i  Index xs ] × Index (k (lookup xs i))

lookup∙ : (xs : Syntax A) (k : A  Syntax B)  >>=∙-ind xs k  B
lookup∙ xs k = uncurry (lookup ∘′ k ∘′ lookup xs)

module _ (k : A  Syntax B) where
  pull-shape : (s : Syntax A)  >>=-ind s k  >>=∙-ind s k
  pull-shape = elim-s _ λ { (var x) i  tt , i ; (op (O , k) P⟨xs⟩) (i , is)  let j , js = P⟨xs⟩ i is in (i , j) , js }

  push-shape : (s : Syntax A)  >>=∙-ind s k  >>=-ind s k
  push-shape = elim-s _ λ { (var x) (i , is)  is ; (op (O , k) P⟨xs⟩) ((i , is) , js)  i , P⟨xs⟩ i (is , js) }

  push∘pull : (s : Syntax A) (i : >>=-ind s k)  push-shape s (pull-shape s i)  i
  push∘pull = elim-s _ λ { (var x) _  refl ; (op (O , k) P⟨xs⟩) (i , is)  cong (i ,_) (P⟨xs⟩ i is) }

  pull∘push : (s : Syntax A) (i : >>=∙-ind s k)  pull-shape s (push-shape s i)  i
  pull∘push =
    elim-s _ λ { (var x) _  refl
               ; (op (O , k) P⟨xs⟩) ((i , is) , js) p   let (j′ , js′) = P⟨xs⟩ i (is , js) p in ((i , j′) , js′) }

  lookup-push : (s : Syntax A) (i : Index s) (is : Index (k (lookup s i)))  lookup (k (lookup s i)) is  lookup (s >>= k) (push-shape s (i , is))
  lookup-push = elim-s _ λ { (var x) i is  refl
                           ; (op (O , k) P⟨xs⟩) (i , is′) is  P⟨xs⟩ i is′ is
                           }

  lookup-pull : (s : Syntax A) (i : Index (s >>= k))  uncurry (lookup ∘′ k ∘′ lookup s) (pull-shape s i)  lookup (s >>= k) i
  lookup-pull = elim-s _ λ { (var x) i  refl
                           ; (op (O , k) P⟨xs⟩) (i , is′)  P⟨xs⟩ i is′
                           }

>>=⟦⟧ :  (k : A  Syntax B) (f :  x  Index (k x)  C) (xs : Syntax A)
       Indexed .inv (shape (xs >>= k) , uncurry (f ∘′ lookup xs)  pull-shape k xs)  xs >>= fill _ ∘′ f
>>=⟦⟧ k f = elim-s _
  λ { (var x)  refl ; (op (O , _) P⟨xs⟩)  cong (op  _,_ O) (funExt P⟨xs⟩) }