File: Wrap.agda

package info (click to toggle)
agda-stdlib 2.1-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,196 kB
  • sloc: haskell: 375; makefile: 32; sh: 28; lisp: 1
file content (193 lines) | stat: -rw-r--r-- 7,531 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
------------------------------------------------------------------------
-- The Agda standard library
--
-- An example of how to use `Wrap` to help term inference.
------------------------------------------------------------------------

{-# OPTIONS --cubical-compatible --safe #-}

module README.Data.Wrap where

open import Data.Wrap

open import Algebra
open import Data.Nat
open import Data.Nat.Properties
open import Data.Product.Base using (_,_; ∃; ∃₂; _×_)
open import Level using (Level)

private
  variable
    c ℓ : Level
    A : Set c
    m n : ℕ

------------------------------------------------------------------------
-- `Wrap` for remembering instances
------------------------------------------------------------------------

module Instances where

  -- `Monoid.Carrier` gets the carrier set from a monoid, and thus has
  -- type `Monoid c ℓ → Set c`.
  -- Using `Wrap`, we can convert `Monoid.Carrier` into an equivalent
  -- “wrapped” version: `MonoidEl`.
  MonoidEl : Monoid c ℓ → Set c
  MonoidEl = Wrap Monoid.Carrier

  -- We can turn any monoid into the equivalent monoid where the elements
  -- and equations have been wrapped.
  -- The translation mainly consists of wrapping and unwrapping everything
  -- via the `Wrap` constructor, `[_]`.
  -- Notice that the equality field is wrapping the binary relation
  -- `_≈_ : (x y : Carrier) → Set ℓ`, giving an example of how `Wrap` works
  -- for arbitrary n-ary relations.
  Wrap-monoid : Monoid c ℓ → Monoid c ℓ
  Wrap-monoid M = record
    { Carrier = MonoidEl M
    ; _≈_ = λ ([ x ]) ([ y ]) → Wrap _≈_ x y
    ; _∙_ = λ ([ x ]) ([ y ]) → [ x ∙ y ]
    ; ε = [ ε ]
    ; isMonoid = record
      { isSemigroup = record
        { isMagma = record
          { isEquivalence = record
            { refl = [ refl ]
            ; sym = λ ([ xy ]) → [ sym xy ]
            ; trans = λ ([ xy ]) ([ yz ]) → [ trans xy yz ]
            }
          ; ∙-cong = λ ([ xx ]) ([ yy ]) → [ ∙-cong xx yy ]
          }
        ; assoc = λ ([ x ]) ([ y ]) ([ z ]) → [ assoc x y z ]
        }
      ; identity = (λ ([ x ]) → [ identityˡ x ])
                 , (λ ([ x ]) → [ identityʳ x ])
      }
    }
    where open Monoid M

  -- Usually, we would only open one monoid at a time.
  -- If we were to open two monoids `M` and `N` simultaneously, Agda would
  -- get confused whenever it came across, for example, `_∙_`, not knowing
  -- whether it came from `M` or `N`.
  -- This is true whether or not `M` and `N` can be disambiguated by some
  -- other means (such as by their `Carrier`s).

  -- However, with wrapped monoids, we are going to remember the monoid
  -- while checking any monoid expressions, so we can afford to have just
  -- one, polymorphic, version of `_∙_` visible globally.
  open module Wrap-monoid {c ℓ} {M : Monoid c ℓ} = Monoid (Wrap-monoid M)

  -- Now we can test out this construct on some existing monoids.

  open import Data.Nat.Properties

  -- Notice that, while the following two definitions appear to be defined
  -- by the same expression, their types are genuinely different.
  -- Whereas `Carrier +-0-monoid = ℕ = Carrier *-1-monoid`, `MonoidEl M`
  -- does not compute, and thus
  -- `MonoidEl +-0-monoid ≠ MonoidEl *-1-monoid` definitionally.
  -- This lets us use the respective monoids when checking the respective
  -- definitions.

  test-+ : MonoidEl +-0-monoid
  test-+ = ([ 3 ] ∙ ε) ∙ [ 2 ]

  test-* : MonoidEl *-1-monoid
  test-* = ([ 3 ] ∙ ε) ∙ [ 2 ]

  -- The reader is invited to normalise these two definitions
  -- (`C-c C-n`, then type in the name).
  -- `test-+` is interpreted using (ℕ, +, 0), and thus computes to `[ 5 ]`.
  -- Meanwhile, `test-*` is interpreted using (ℕ, *, 1), and thus computes
  -- to `[ 6 ]`.

------------------------------------------------------------------------
-- `Wrap` for dealing with functions spoiling unification
------------------------------------------------------------------------

module Unification where

  open import Relation.Binary.PropositionalEquality

  module Naïve where

    -- We want to work with factorisations of natural numbers in a
    -- “proof-relevant” style. We could draw out `Factor m n o` as
    --   m
    --  /*\
    -- n   o.

    Factor : (m n o : ℕ) → Set
    Factor m n o = m ≡ n * o

    -- We can prove a basic lemma about `Factor`: the following tree rotation
    -- can be done, due to associativity of `_*_`.
    --      m             m
    --     /*\           /*\
    --   no   p  ---->  n   op
    --  /*\                 /*\
    -- n   o               o   p

    assoc-→ : ∀ {m n o p} →
              (∃ λ no → Factor m no p × Factor no n o) →
              (∃ λ op → Factor m n op × Factor op o p)
    assoc-→ {m} {n} {o} {p} (._ , refl , refl) = _ , *-assoc n o p , refl

    -- We must give at least some arguments to `*-assoc`, as Agda is unable to
    -- unify `? * ? * ?` with `n * o * p`, as `_*_` is a function and not
    -- necessarily injective (and indeed not injective when one of its
    -- arguments is 0).

    -- We now want to use this lemma in a more complex proof:
    --         m            m
    --        /*\          /*\
    --     nop   q        n   opq
    --     /*\      ---->     /*\
    --   no   p              o   pq
    --  /*\                      /*\
    -- n   o                    p   q

    test : ∀ {m n o p q} →
           (∃₂ λ no nop → Factor m nop q × Factor nop no p × Factor no n o) →
           (∃₂ λ pq opq → Factor m n opq × Factor opq o pq × Factor pq p q)
    test {n = n} (no , nop , fm , fnop , fno) =
      let _ , fm , fpq = assoc-→ {n = no} (_ , fm , fnop) in
      let _ , fm , fopq = assoc-→ {n = n} (_ , fm , fno) in
      _ , _ , fm , fopq , fpq

    -- This works okay, but where we have written `{n = no}` and similar, we
    -- are being forced to deal with details we don't really care about. Agda
    -- should be able to fill in the vertices given part of a tree, but can't
    -- due to similar reasons as before: `Factor ? ? ?` doesn't unify against
    -- `Factor m no p`, because both instances of `Factor` compute and we're
    -- left trying to unify `? * ?` against `no * p`.

  module Wrapped where

    -- We can use `Wrap` to stop the computation of `Factor`.

    Factor : (m n o : ℕ) → Set
    Factor = Wrap λ m n o → m ≡ n * o

    -- Because `assoc-→` needs access to the implementation of `Factor`, the
    -- proof is exactly as before except for using `[_]` to wrap and unwrap.

    assoc-→ : ∀ {m n o p} →
              (∃ λ no → Factor m no p × Factor no n o) →
              (∃ λ op → Factor m n op × Factor op o p)
    assoc-→ {m} {n} {o} {p} (._ , [ refl ] , [ refl ]) =
      _ , [ *-assoc n o p ] , [ refl ]

    -- The difference is that now we have our basic lemma, the complex proof
    -- can work purely in terms of `Factor` trees. In particular,
    -- `Factor ? ? ?` now does unify with `Factor m no p`, so we don't have to
    -- give `no` explicitly again.

    test : ∀ {m n o p q} →
           (∃₂ λ no nop → Factor m nop q × Factor nop no p × Factor no n o) →
           (∃₂ λ pq opq → Factor m n opq × Factor opq o pq × Factor pq p q)
    test (_ , _ , fm , fnop , fno) =
      let _ , fm , fpq = assoc-→ (_ , fm , fnop) in
      let _ , fm , fopq = assoc-→ (_ , fm , fno) in
      _ , _ , fm , fopq , fpq