The chapter "7.3.2. Deriving Derivatives by Data Types" "Cosmology of Data Types" shows how to output operations using descriptions. Although, the resulting Eq
rather weak.
The main idea is to represent data types using some first-order encoding, that is, in terms of some general data type, and to define operations on this data type, so everything encoded in terms of this can be processed by these generic operations. I developed a simple version of this mechanism here .
You can get a stronger Eq
if you have a closed universe. Using an approach similar to the description (it should be equally expressive, but I didn’t check it) and the closed universe, I defined a general show
here , which allows, for example, to print the vector of tuples after the name of the constructors:
instance named-vec : {A : Type} -> Named (vec-cs A) named-vec = record { names = "nil" ∷ "cons" ∷ [] } test₂ : show (Vec (nat & nat) 3 ∋ (7 , 8) ∷ᵥ (9 , 10) ∷ᵥ (11 , 12) ∷ᵥ []ᵥ) ≡ "(cons 2 (7 , 8) (cons 1 (9 , 10) (cons 0 (11 , 12) nil)))" test₂ = prefl
where Vec
defined in terms of a data type of type Desc
. The case of Eq
should be similar, but more complex.
Here's how to use Lift
:
⟦_⟧Type : Type → Set₁ ⟦ Nat ⟧Type = Lift ℕ ⟦ Prp ⟧Type = Set ex₁ : ∀ A -> ⟦ A ⟧Type ex₁ Nat = lift 0 ex₁ Prp = ℕ ex₂ : ∀ A -> ⟦ A ⟧Type -> Maybe ℕ ex₂ Nat n = just (lower n)
If A : Set α
, then Lift A : Set (α ⊔ ℓ)
for any ℓ
. Therefore, when you have ℕ : Set
and Set : Set₁
, you want to raise ℕ
from Set
to Set₁
, which is just Lift ℕ
- in simple cases you do not need to explicitly specify ℓ
.
To create a data type element wrapped in Lift
, you use Lift
(for example, in lift 0
). And to return this element, use lower
, so Lift
and lower
are inverse to each other. Note that lift (lower x)
does not necessarily lie in the same universe as x
, because lift (lower x)
“updates” ℓ
.