You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: plutus-metatheory/src/VerifiedCompilation/Equality.lagda.md
+68-28Lines changed: 68 additions & 28 deletions
Original file line number
Diff line number
Diff line change
@@ -148,40 +148,51 @@ decEq-TyTag (_⊢♯.pair t t₁) (_⊢♯.pair t' t'') with (t ≟ t') ×-dec (
148
148
... | no ¬pq = no λ { refl → ¬pq (refl , refl) }
149
149
150
150
```
151
-
The equality of the semantics of constants will depend on the equality of
152
-
the underlying types, so this can leverage the standard library decision
153
-
procedures.
151
+
# Decidable Equality of Builtins
154
152
155
-
```
156
-
record HsEq (A : Set) : Set where
157
-
field
158
-
hsEq : A → A → Agda.Builtin.Bool.Bool
153
+
We need to decide equality between our builtin types. This is tricky because
154
+
this needs to be done at both the Agda type-checking time and at runtime, while
155
+
each stage has a completely different representation of the types.
159
156
160
-
open HsEq {{...}} public
157
+
## Type-checking time vs runtime
161
158
162
-
postulate
163
-
magicNeg : ∀ {A : Set} {a b : A} → ¬ a ≡ b
159
+
In Agda, the types are postulated, which means that at type-checking time we
160
+
may only rely on Agda's unification algorithm to decide equality. This can be
161
+
done by matching on `refl`, which checks whether the left hand side and the
162
+
right hand side of `≡` are definitionally equal. However, this does not translate
163
+
to the runtime stage, since at runtime the values which the `≡` type depends on
164
+
are erased. Therefore, we need to somehow "inject" a Haskell equality function which
165
+
triggers only at the runtime stage.
164
166
165
-
magicBoolDec : {A : Set} → {a b : A} → Agda.Builtin.Bool.Bool → Dec (a ≡ b)
166
-
magicBoolDec true = yes primTrustMe
167
-
magicBoolDec false = no magicNeg
168
-
```
167
+
## Why not just implement the builtin types in Agda?
168
+
169
+
The problem is that Agda's FFI only allows non-postulated Agda types which are
170
+
representationally equivalent to the Haskell types they compile to. If we were to
171
+
implement the types in Agda, they would need to be equivalent to the highly optimized
172
+
and complicated Haskell types, and this is not feasible.
173
+
174
+
We also cannot de-couple the Agda types from the Haskell types because the Agda
175
+
specification of UPLC is also used in conformance testing.
176
+
177
+
## Using the quirks of the FFI to our advantage
169
178
170
-
Our builtins types and functions are postulated. In order to decide equality
171
-
we rely on Agda's notion of definitional equality.
179
+
Agda's FFI machinery allows us to define functions with different runtime
180
+
and type-checking definitions (see the warning at https://agda.readthedocs.io/en/v2.7.0.1/language/foreign-function-interface.html#using-haskell-functions-from-agda).
181
+
We are still constrained by the type, which needs to agree between the two
182
+
stages, so we can't just define the two implementations arbitrarily.
172
183
173
-
The definition of `builtinEq` might seem strange, but what happens is that
174
-
matching on `refl` triggers Agda's unification algorithm, which checks whether
175
-
the two terms are definitionally equal.
184
+
The simplest solution is to provide separate type-checking time and runtime definitions
185
+
for the instances of `HsEq`. During type-checking, the functions are essentially no-ops
186
+
by always returning `true`, while at runtime they defer to the Haskell implementation of
187
+
equality for each type. At type-checking time, we rely on matching on `refl` to defer to
188
+
Agda's unification algorithm, while at runtime, the matching on `refl` becomes a no-op.
176
189
177
-
For example: for `builtinEq (mkByteString "foo") (mkByteString "foo")` the two terms
178
-
are structurally equal so unification will succeed, and the function will return
179
-
`yes refl`, while `builtinEq (mkByteString "foo") (mkByteString "bar")` will get
0 commit comments