[Agda] Why does Agda think this is not strictly positive?

Roman effectfully at gmail.com
Wed Jul 20 15:23:51 CEST 2016


> the problem seems to be that Agda
> cannot see that all constructors that target Coerce β A use A in a
> strictly positive way, since A is not a fixed parameter.

Ah, it didn't come to my mind that Agda treats parameters and indices
differently wrt positivity checking.

> Now, since B is fixed, it is easy for Agda to see that B is always used
> strictly positively, but this is probably not what you intended... (Of
> course, being able to coerce from Set α to Set β for arbitrary α and β
> would be too good to be true.)

I'm not trying to coerce from `Set α` to `Set β` for arbitrary `α` and
`β`, of course. The idea is the same as with propositional equality:
in `p : x ≡ y` `x` and `y` can be syntactically distinct terms, but
once you pattern match on `p` they will be unified. `Coerce` allows to
kinda postpone universes check: you can write `Coerce β A` as an
argument to a constructor and no matter where `A` lies, `Coerce β A`
will lie in `Set β`. The actual universes check will be performed when
constructing an element of `Coerce β A`.

Using `Coerce` we can define a type of universe polymorphic descriptions ([1]):

    {-# NO_POSITIVITY_CHECK #-}
    data Desc {ι} (I : Set ι) β : Set (ι ⊔ lsuc β) where
      var : I -> Desc I β
      π   : ∀ {α}
          -> Coerce (ι ⊔ lsuc β) (∃ λ (A : Set α) -> A -> Desc I β)
          -> Desc I β
      _⊛_ : Desc I β -> Desc I β -> Desc I β

The type signature of `π` encodes the fact that `lsuc α` is less or
equal than `ι ⊔ lsuc β`.

As you noted `Coerce β A` implies `α ≡ β`, which in the case of `π` means that

    lsuc α ⊔ ι ⊔ lsuc β ≡ ι ⊔ lsuc β

which is the same as `lsuc α ≤ ι ⊔ lsuc β` (if there would be `_≤_`
defined on levels). So we have a first-class encoding of cumulativity
as witnessed by the following example:

    pattern pi A D = π (coerce (A , D))

    open import Data.Nat.Base

    Vec : ∀ {α} -> Set α -> Desc ℕ α
    Vec A = pi Bool λ b -> if b
              then var 0
              else (pi ℕ λ n -> pi A λ _ -> var n ⊛ var (suc n))

In the second constructor of `Vec` one `pi` receives `ℕ` from `Set`
and another `pi` receives `A` from `Set α`, nevertheless the
definition is type checked, because both `lzero` and `α` are less or
equal than `α`.

`Coerce` can be defined as a function

    Coerce : ∀ {α β} -> α ≡ β -> Set α -> Set β
    Coerce refl = id

and then used like this

    data Desc {ι} (I : Set ι) β : Set (ι ⊔ lsuc β) where
      var     : I -> Desc I β
      π       : ∀ {α} {{q : α ⊔ β ≡ β}}
             -> Coerce (cong (λ αβ -> ι ⊔ lsuc αβ) q) (∃ λ (A : Set α)
-> A -> Desc I β)
             -> Desc I β
      _⊛_ : Desc I β -> Desc I β -> Desc I β

I'm using a similar definition in some actual code ([1]), but a
function confuses the termination checker, so I would like to switch
to `Coerce` defined as a data type.

[1] https://github.com/effectfully/Generic


More information about the Agda mailing list