module Description where

open import Prelude

open import Data.Unit using (; tt)
open import Data.Product using (Σ; _,_; _×_)


data RDesc (I : Set) : Set₁ where
     : RDesc I
  ṿ   : (i : I)  RDesc I
  σ   : (S : Set) (D : S  RDesc I)  RDesc I
  _*_ : (D E : RDesc I)  RDesc I

syntax σ S  s  D) = σ[ s ∶ S ] D

⟦_⟧ :  {I}  RDesc I  (I  Set)  Set
       X = 
 ṿ i    X = X i
 σ S D  X = Σ[ s  S ]  D s  X
 D * E  X =  D  X ×  E  X

data Desc (I : Set) : Set₁ where
  wrap : (I  RDesc I)  Desc I

_at_ :  {I}  Desc I  I  RDesc I
(wrap D) at i = D i

 :  {I}  Desc I  (I  Set)  (I  Set)
 D X i =  D at i  X

_⇒_ :  {I}  (I  Set)  (I  Set)  Set
X  Y =  {i}  X i  Y i

infixr 1 _⇒_

data μ {I} (D : Desc I) : I  Set where
  con :  D (μ D)  μ D

decon :  {I} {D : Desc I}  μ D   D (μ D)
decon (con xs) = xs

-- fold

mutual

  fold :  {I X} {D : Desc I}   D X  X  μ D  X
  fold {D = D} φ {i} (con ds) = φ (mapFold D (D at i) φ ds)

  mapFold :  {I} (D : Desc I) (E : RDesc I)   {X}  ( D X  X)   E  (μ D)   E  X
  mapFold D         φ _          = _
  mapFold D (ṿ i)    φ d          = fold φ d
  mapFold D (σ S E)  φ (s , ds)   = s , mapFold D (E s) φ ds
  mapFold D (E * E') φ (ds , ds') = mapFold D E φ ds , mapFold D E' φ ds'

-- induction

All :  {I} (D : RDesc I) {X : I  Set} (P :  {i}  X i  Set)   D  X  Set
All        P _          = 
All (ṿ i)   P x          = P x
All (σ S D) P (s , xs)   = All (D s) P xs
All (D * E) P (xs , xs') = All D P xs × All E P xs'

mutual

  induction :  {I} (D : Desc I) (P :  {i}  μ D i  Set) 
              (∀ {i} (ds :  D (μ D) i)  All (D at i) P ds  P (con ds)) 
               {i} (d : μ D i)  P d
  induction D P ih {i} (con ds) = ih ds (everywhereInduction D (D at i) P ih ds)

  everywhereInduction :
     {I} (D : Desc I) (E : RDesc I)
    (P :  {i}  μ D i  Set) 
    (∀ {i} (ds :  D (μ D) i)  All (D at i) P ds  P (con ds)) 
    (ds :  E  (μ D))  All E P ds
  everywhereInduction D         P ih _          = _
  everywhereInduction D (ṿ i)    P ih d          = induction D P ih d
  everywhereInduction D (σ S E)  P ih (s , ds)   = everywhereInduction D (E s) P ih ds
  everywhereInduction D (E * E') P ih (ds , ds') = everywhereInduction D E P ih ds ,
                                                   everywhereInduction D E' P ih ds'