I've been working for a couple of weeks on an Agda project, glibly ignoring level polymorphism as much as I can. Unfortunately (or perhaps fortunately) I seem to have reached the point where I need to start understanding it.
Until now I've been using level variables only when they are needed as a second argument to Rel
(or third argument to REL
). Otherwise I have omitted them, just using Set
directly. Now I have some client code that explicitly quantifies over levels a
and tries to pass some types of the form Set a
to my existing code, which is now insufficiently polymorphic. In the example below, quibble
represents the client code, and _[_]×[_]_
and ≈-List
are typical of my existing code which just uses Set
.
module Temp where
open import Data.List
open import Data.Product
open import Level
open import Relation.Binary
-- Direct product of binary relations.
_[_]×[_]_ : {ℓ₁ ℓ₂ : Level} {A B C D : Set} → A × B → REL A C ℓ₁ → REL B D ℓ₂ → C × D → Set (ℓ₁ ⊔ ℓ₂)
(a , b) [ _R_ ]×[ _S_ ] (c , d) = a R c × b S d
-- Extend a setoid (A, ≈) to List A.
data ≈-List {ℓ : Level} {A : Set} (_≈_ : Rel A ℓ) : Rel (List A) ℓ where
[] : ≈-List _≈_ [] []
_∷_ : {x y : A} {xs ys : List A} → (x≈y : x ≈ y) → (xs≈ys : ≈-List _≈_ xs ys) → ≈-List _≈_ (x ∷ xs) (y ∷ ys)
quibble : {a ℓ : Level} {A : Set a} → Rel A ℓ → Rel (List (A × A)) ℓ
quibble _≈_ = ≈-List (λ x y → x [ _≈_ ]×[ _≈_ ] y)
Here, I can extend the inductive definition of ≈-List
with an extra level parameter a
so that it can take a type argument of type Set a
, but then I'm unclear as to how the universes of the input and output relations should change. And then problem spills out to more complex definitions such as _[_]×[_]_
where I'm even less sure how to proceed.
How should I generalise the signatures in the example given, so that quibble
compiles? Are there general rules I can follow? I've read this.