Following definition compiles and behaves well:
data Eq {lvl} {A : Set lvl} (x : A) : A → Set where
refl : Eq x x
However this one does not compile:
data Eq {lvl} {A : Set lvl} (x : A) (y : A) : Set where
refl : Eq x x
because
x != y of type A
when checking the constructor refl in the declaration of Eq
I don't understand why they aren't equivalent. What's the difference and why the second variant is not correct?
The arguments on the left of the colon are called parameters; those on the right are called indices. The difference between the two is this: In the return type of the data type's constructors, the parameters must always be exactly the variables from the type declaration. The indices, on the other hand, can be arbitrary terms (of the correct type).
In your second example, the return type of
reflisEq x x. However,yis a parameter, so the return type would have to beEq x y. In your first example, the type ofreflis legal becauseyis an index andxis a term of typeA.By 'return type' I mean the expression to the right of the last arrow in a constructor's type. To illustrate, here is the definition of length-indexed lists aka vectors:
The return type of
_∷_isVec A (suc n). Again,suc nis an arbitrary expression of typeℕ. If the data type occurs in the type of an argument of a constructor, as with theVec A nargument to_∷_, both the parameters and the indexes can be arbitrary terms (though we use the same parameter here).When you pattern match on an indexed data type, the constructor's index can give you additional information. Consider
headon vectors:We don't need to write an equation for the constructor
[]because its type isVec A zerowhereas the type of the pattern isVec A (suc n)and these types cannot be equal. Similarly, consider the following proof that your equality is symmetric:By pattern matching on
refl, we learn thatyis, in fact,x. This is indicated by the dot pattern.x. Thus, our goal becomesx ≡ x, which can be proven withreflagain. More formally,xis unified withywhen we match onrefl.You might wonder whether you should simply declare all arguments of a data type as indices. I believe that in Agda, there's no downside to doing so, but it's good style to declare arguments as parameters if possible (since these are simpler).
Related: section of the Agda manual; SO question with more examples.