{-# OPTIONS --without-K #-}
module FunUniverse.Data where

open import Type hiding ()
open import Level.NP
open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym; subst)
open import Function
open import Data.Nat.NP  using (; _+_; _*_; _*′_; _^_; module ℕ°)
open import Data.One     using (𝟙)
open import Data.Two     using (𝟚)
open import Data.Product using (Σ; _×_; _,_; proj₂) renaming (zip to _`zip`_)
open import Data.Vec     using (Vec)

-- An interface for small, finite & discrete universes of types.
record Universe {t} (T :  t) :  t where
  constructor mk
  field
    `𝟙   : T
    `𝟚   : T
    _`×_ : T  T  T
    _`^_ : T    T

  `Vec : T    T
  `Vec A n = A `^ n

  `Bit : T
  `Bit = `𝟚

  `Bits :   T
  `Bits n = `Bit `^ n

  infixr 2 _`×_
  infixl 2 _`^_

-- In ★-U, types are simply represented by Agda types (★ or Set).
★-U : Universe ★₀
★-U = mk 𝟙 𝟚 _×_ Vec

-- In Bits-U, a type is represented by a natural number
-- representing the width of the type in a binary representation.
-- A natural embedding in ★ is the Bits type (aka Vec Bool).
Bits-U : Universe 
Bits-U = mk 0 1 _+_ (flip _*_)

module Cong-*1 {} (_`→_ :      ) {i o} where
    eq-*1 : i `→ o  (i * 1) `→ (o * 1)
    eq-*1 rewrite proj₂ ℕ°.*-identity i
                | proj₂ ℕ°.*-identity o = refl

    cong-*1 : i `→ o  (i * 1) `→ (o * 1)
    cong-*1 = subst id eq-*1

    cong-*1′ : (i * 1) `→ (o * 1)  i `→ o
    cong-*1′ = subst id (sym eq-*1)

-- In Fin-U, a type is represented by a natural number
-- representing the cardinality of the type.
-- A natural embedding in ★ is the Fin type.
Fin-U : Universe 
Fin-U = mk 1 2 _*_ _^_

-- In 𝟙-U, there is only one possible type.
𝟙-U : Universe 𝟙
𝟙-U = _ -- Agda figures out that there is only one such universe

𝟙-U-uniq : {U₀ U₁ : Universe 𝟙}  U₀  U₁
𝟙-U-uniq = refl

-- Take the product of two universes. All types have two components, one from
-- each of the forming universes.
×-U :  {s t} {S :  s} {T :  t}  Universe S  Universe T  Universe (S × T)
×-U S-U T-U = mk (S.`𝟙 , T.`𝟙) (S.`𝟚 , T.`𝟚) (S._`×_ `zip` T._`×_)
                  { (A₀ , A₁) n  S.`Vec A₀ n , T.`Vec A₁ n })
  where module S = Universe S-U
        module T = Universe T-U

-- Sym-U is a “symantic” (a mix of syntax and semantics) representation
-- for types. Symantic types are those defined only in term of the
-- Universe interface. [See the “Finally Tagless” approach by Oleg Kiselyov.]
Sym-U :  t   ( t)
Sym-U t =  {T :  t}  Universe T  T

-- Abstract syntax tree from types.
data Syn : ★₀ where
  𝟙′ 𝟚′ : Syn
  _×′_  : Syn  Syn  Syn
  _^′_  : Syn    Syn

-- Ty-U is the universe of the syntactic represented types.
Syn-U : Universe Syn
Syn-U = mk 𝟙′ 𝟚′ _×′_ _^′_

-- Turn a syntactic type into a symantic one.
-- Alternatively:
--   * a syntactic type is turned into a type of any $given universe.
--   * the catamorphism for syntactic types.
fold-U :  {t}  Syn  Sym-U t
fold-U u₀ {T} uni = go u₀
  where open Universe uni
        go : Syn  T
        go 𝟙′         = `𝟙
        go 𝟚′         = `𝟚
        go (t₀ ×′ t₁) = go t₀  go t₁
        go (t ^′ n)   = go t `^ n

-- The type of universe unary operators or universe transformers.
UniOp :  {s t} (S :  s) (T :  t)   _
UniOp S T = Universe S  Universe T

-- The type of universe binary operators.
UniOp₂ :  {r s t} (R :  r) (S :  s) (T :  t)   _
UniOp₂ R S T = Universe R  Universe S  Universe T