Do-it-yourself Module Systems

Unbundling on the fly and mechanically obtaining termtypes from theories


Agda Implementors Meeting 2020


Musa Al-hassy
⟪ Flattened View ; Press ? for Help ⟫

Overview

With a bit of reflection, we can obtain

  1. a uniform, and practical, syntax for both records (semantics) and termtypes (syntax)
  2. on-the-fly unbundling; and,
  3. mechanically obtain data structures from theories

    ‘theory’ τ ‘data structure’ termtype τ
    pointed set 𝟙
    dynamic system
    monoid tree skeletons
    collections lists
    graphs (homogeneous) pairs
    actions infinite streams

Unbundling Problem

Monoid —“Untyped Composition”

  • We have things, called Carrier,
  • … and we want to ‘combine’ them via some operation __
  • … which has a ‘do nothing’ value Id.

Typeclass approach, as in Haskell

record MonoidOn (Carrier : Set) : Set
 where
  field
    __ : Carrier  Carrier  Carrier
    Id  : Carrier
    leftId :  x  Id  x  x
    rightId :  x  x  Id  x
    assoc   :  x y z   (x  y)  z
                         x  (y  z)
open MonoidOn {{...}}

Examples

  • Programs and sequential (or parallel) composition
  • Numbers and addition (or multiplication)
  • Writing words on a page —i.e., lists and catenation

What is “the” monoid on the natural numbers?

Additive?

instance
   : MonoidOn 
   = record { __     = _+_
              ; Id      = 0
              ; leftId  = +-identityˡ
              ; rightId = +-identityʳ
              ;  assoc  = +-assoc }
ex :  (m n : )  m  n    n  m
ex = +-comm

____ is determined to be _+_by instance search

likewise :  (m : )  m  Id  m
likewise = rightId

… or Multiplicative?

instance
  ℕₓ : MonoidOn 
  ℕₓ = record { __     = _*_
              ; Id      = 1
              ; leftId  = *-identityˡ
              ; rightId = *-identityʳ
              ; assoc   = *-assoc }
whoops :  {m : }  m  Id  m
whoops = {!!}

⇨ ℕ₊ and ℕₓ are both candidates! No unique solution!

Haskell's Solution

Make two isomorphic copies of numbers …

Some types can be viewed as a monoid in more than one way, e.g. both addition and multiplication on numbers. In such cases we often define newtypes and make those instances of Monoid, e.g. Sum and Product. ---Hackage Data.Monoid

Sum α  α  {- and -} Product α  α

For Num α they have different monoid instances.

Unbundling in Agda

Start with fully bundled Monoid

then expose fields as parameters on the fly.

How?

Reflection!

  • Unfortunately, current mechanism cannot touch record-s directly.
  • But every record is a Σ-type (•̀ᴗ•́)و

Records as ΠΣ-types

  • Instead of the nice syntactic sugar

    record R (ε¹ : τ¹)  (εʷ : τʷ) : Set
      where
        field
          εʷ¹ : τʷ¹
          
          εʷ: τʷ
  • Use a more raw form ---eek!

    R    Π ε¹ : τ¹    Π εʷ : τʷ
         Σ εʷ¹ : τʷ¹    Σ εʷ: τʷ 𝟙
    

Terminology

R    Π ε¹ : τ¹    Π εʷ : τʷ
     Σ εʷ¹ : τʷ¹    Σ εʷ: τʷ 𝟙

“R is a ΠʷΣ type” —a (partitioned) context

⇨ It has parameters ε⁰, …, εʷ and fields εʷ⁺¹, …, εʷ⁺ᵏ

⇨ \(w\) is the “waist”

E.g., MonoidOn is a Π¹Σ type

Shucks, the Unbundling Problem Strikes Again

If we encode a record as a ΠʷΣ type, what if we want to instantiate, fix, a field —instead of a parameter?

We thus need a way to lift parameters to fields!

( Teaser: Πλ ! )

Pragmatic Notation for Contexts

Instead of Set, use (waist : ) Set

  1. “Contexts” are exposure-indexed types

    Context = λ ℓ    Set
  2. The “empty context” is the unit type

    End :  {ℓ}  ContextEnd {ℓ} =  𝟙 {ℓ}
    
  3. do-notation!

    _>>=_ :  {a b}
           (Γ : Context a)
           ( {n}  Γ n  Context b)
           Context (a  b)
    (Γ >>= f) zero    = Σ γ  Γ 0  f γ 0
    (Γ >>= f) (suc n) = Π γ  Γ n  f γ n
    
  4. The “DIY” lies at >>=, permitting Σ, Π, 𝒲, let, … !

Example Context —Monoids

Monoid : Context ℓ₁
Monoid = do Carrier  Set
            __      (Carrier  Carrier  Carrier)
            Id       Carrier
            leftId    (x : Carrier)  x  Id  x
            rightId   (x : Carrier)  Id  x  x
            assoc     (x y z)  (x  y)  z    x  (y  z)
            End {ℓ}

Example Instance —Additive Naturals

₊′ : (Monoid ℓ₀ :waist 1) 
₊′ =_+_           -- _⨾_
      , 0             -- Id
      , +-identityˡ
      , +-identityʳ
      , +-assoc
      ⟩

Notice…

(Monoid ℓ₀ :waist 1)     MonoidOn 

Πλ —or _:waist_

 
record
Context
exposure-indexed type

Using   Contexts —‘reification’

  • If C : Context ℓ₀ then C w : Πʷ Set, but we want to apply C w to w-many parameters?

    (Πʷ x • τ) is w-many Π's binding variables to be used in type τ.

  • So we need a combinator…

     Πλ  “Πʷ x  τ”   =   “λʷ x  τ”
    
  • with an infix form for contexts in particular …

    C :waist w   =   Πλ (C w)
    

Example

Monoid : Context
Monoid = do C  Set; __ : C  C  C; Id  C; 

With no parameters, we have a Π⁰Σ-type

Monoid :waist 0  : SetMonoid :waist 0    Σ C : Set  Σ __ : C  C  C  Σ Id : C  

With one parameter, we regain MonoidOn

Monoid :waist 1  :  Π C : Set  Set
Monoid :waist 1  =  λ C : Set  Σ __ : C  C  C  Σ Id : C  

With two parameters, we have a ‘solution’ to the additive-or-multiplicative-monoid-problem!

Monoid :waist 2  :  Π C : Set)  Π __ : C  C  C  Set
Monoid :waist 2  =  λ C : Set  λ __ : C  C  C  Σ Id : C  

Relationships between various forms

Monoid : Context -- i.e., (w : ℕ) → Set

Monoid :waist 𝓌   :  Πʷ   Set
Monoid :waist 𝓌   =  λʷ “parameters”  “fields”

11 Line Implementation —thanks Ulf!

The Core Syntactic Transformation

--  Π a : A • B a   ↦   λ a : A • B a

Πλ-helper : Term  Term

Πλ-helper (pi a (abs x b))
  = lam visible (abs x (Πλ-helper b))

Πλ-helper x
  = x

Keeping Track of Types

Term denotes untyped λ-terms, so let's keep track of the types when we convert Πs to λs.

Πλ-type : Term  Term

Πλ-type (pi a (abs x b))
 = pi a  (abs x (Πλ-type b))

Πλ-type x
 = unknown

Putting them together

macro
  Πλ : Term  Term  TC Unit.⊤
  Πλ τ goal
   =  normalise τ
       >>=ₜₑᵣₘ λ τ  checkType goal (Πλ-type τ)
       >>=ₜₑᵣₘ λ _   unify goal (Πλ-helper τ)

Lesson Learned

On-the-fly unbundling can be implemented as an in-language library in a dependently-typed language with sufficient reflection capabilities (•̀ᴗ•́)و

GADTs are Contexts too!

 
Monoid
do C Set; __ : C C C; Id : C;
λ C : Set Σ __ : C C C Σ Id : C
λ C : Set Σ __ : C C C Σ Id : C 𝟙
λ C : Set C × C C 𝟙
μ C : Set C × C C 𝟙

As a macro!

termtype : UnaryFunctor → Type
termtype τ = Fix (Σ→⊎ (sources τ))

macro
  termtype : Term  Term  TC Unit.⊤
  termtype tm goal =
                normalise tm
           >>=ₜₑᵣₘ λ tm  unify goal (def (quote Fix) ((vArg (Σ→⊎₀ (sourcesₜₑᵣₘ tm)))  []))

Monoids give rise to tree skeletons

Context

Monoid :  Context (ℓsuc ℓ)
Monoid= do Carrier  Set__      (Carrier  Carrier  Carrier)
              Id       Carrier
              leftId    {x : Carrier}  Id  x  x
              rightId   {x : Carrier}  x  Id  x
              assoc     {x y z}  (x  y)  z    x  (y  z)
              End {ℓ}

Termtype

  𝕄 : Set
  𝕄 = termtype (Monoid ℓ₀ :waist 1)
  that-is : 𝕄
           FixX 
                -- _⊕_, branch
                X × X × 𝟙
                -- Id, nil leaf
               𝟙
                -- invariant leftId
               𝟘
                -- invariant rightId
               𝟘
                -- invariant assoc
               𝟘
                --  the “End {ℓ}”
               𝟘)
  that-is = refl

Pattern synonyms for more compact presentation

-- : 𝕄
pattern emptyM
    = μ (inj₂ (inj₁ tt))
-- : 𝕄 → 𝕄 → 𝕄
pattern branchM l r
    = μ (inj₁ (l , r , tt))
-- absurd 𝟘-values
pattern absurdM a
    = μ (inj₂ (inj₂ (inj₂ (inj₂ a))))

termtype Monoid ≅ TreeSkeleton

  data TreeSkeleton : Set where
    empty  : TreeSkeleton
    branch : TreeSkeleton  TreeSkeleton  TreeSkeleton
  • “doing nothing”

      to : 𝕄  TreeSkeleton
      to emptyM        = empty
      to (branchM l r) = branch (to l) (to r)
      to (absurdM (inj₁ ()))
      to (absurdM (inj₂ ()))
    
  • “doing nothing”

      from : TreeSkeleton  𝕄
      from empty        = emptyM
      from (branch l r) = branchM (from l) (from r)
    

Dynamical Systems give rise to ℕ

Context

DynamicSystem : Context ℓ₁
DynamicSystem = do State  Set
                   start  State
                   next   (State  State)
                   End {ℓ₀}

Termtype

𝔻 = termtype (DynamicSystem :waist 1)

Pattern synonyms for more compact presentation

-- : 𝔻
pattern startD
    = μ (inj₁ tt)

-- : 𝔻 → 𝔻
pattern nextD e = μ (inj₂ (inj₁ e))

termtype 𝔻 ≅ ℕ

    to : 𝔻  
    to startD    = 0
    to (nextD x) = suc (to x)

    from :   𝔻
    from zero    = startD
    from (suc n) = nextD (from n)

Pointed Sets give rise to terminal types

PSet  : Context (ℓsuc ℓ₀)
PSet  = do Carrier  Set ℓ₀
           point   Carrier
           End {ℓ₀}
ℙ𝕊𝕖𝕥 : Set
ℙ𝕊𝕖𝕥 = termtype (PSet :waist 1)
to : ℙ𝕊𝕖𝕥  𝟙 {ℓ₀}
to emptyM = tt

from : 𝟙 {ℓ₀}  ℙ𝕊𝕖𝕥
from _ = μ (inj₁ tt)

(Simple) Graphs give rise to pairs

Graph  : Context (ℓsuc ℓ₀)
Graph = do Node  Set
           Edge  Set
           adjacency  (Node  Node  Edge)
           End {ℓ₀}
𝔾𝕣𝕒𝕡𝕙 : Set  Set
𝔾𝕣𝕒𝕡𝕙 X = termtype ((Graph :waist 2) X)
pattern __ x y = μ (inj₁ (x , y , tt))

view :  {X}  𝔾𝕣𝕒𝕡𝕙 X  X × X
view (x  y) = x , y

Indexed unary algebras (“actions”) give rise to streams

Context

Action  : Context ℓ₁
Action  = do Value     Set
             Program   Set
             run       (Program  Value  Value)
             End {ℓ₀}

Termtype

𝔸𝕔𝕥𝕚𝕠𝕟 : Set  Set
𝔸𝕔𝕥𝕚𝕠𝕟 X = termtype ((Action :waist 2) X)
-- : X → 𝔸𝕔𝕥𝕚𝕠𝕟 X → 𝔸𝕔𝕥𝕚𝕠𝕟 X
pattern __ head tail
         = μ (inj₁ (tail , head , tt))

… to stream

record Stream (X : Set) : Set   where
  coinductive
  field
    hd : X
    tl : Stream X

open Stream
view :  {I}  𝔸𝕔𝕥𝕚𝕠𝕟 I  Stream I
hd (view (h  t)) = h
tl (view (h  t)) = view t

Collection theories give rise to lists

Collection :  Context (ℓsuc ℓ)
Collection= do Elem       SetContainer  Set ℓ
                  insert     (Elem  Container  Container)
                            Container
                  End {ℓ}
 : Set  Set
 Elem = termtype ((Collection ℓ₀ :waist 2) Elem)
-- : X → ℂ X → ℂ X
pattern _::_ x xs
         = μ (inj₁ (x , xs , tt))

-- : ℂ X
pattern  
    = μ (inj₂ (inj₁ tt))

Summary

‘theory’ τ ‘data structure’ termtype τ
pointed set 𝟙
dynamic system
monoid tree skeletons
collections lists
graphs (homogeneous) pairs
actions infinite streams

Many more theories τ to explore and see what data structures arise!

Bye!

Thanks for listening in!

These slides:

https://alhassy.github.io/next-700-module-systems/diy/agda-implementors-meeting-2020