[Agda] Why the light gray highlighting here?
Thomas Lopatic
thomas at lopatic.de
Wed Sep 30 13:41:44 CEST 2020
Hello there,
I think that the most practical consequence of what Agda is warning you
about is that some applications of _xor_ won't reduce as expected. Let's
normalize something in Emacs based on your definitions:
norm₁ : Bin → Bin
norm₁ y = { ⟨⟩ xor y }0
Normalizing the above goal reduces it to y. That's expected, because
that's the definitional equality given by your first _xor_ clause, which
says that "⟨⟩ xor y" is equal to y by definition.
Now for the highlighted line. Let's normalize again:
norm₂ : Bin → Bin
norm₂ x = { x xor ⟨⟩ }1
We normalize the above goal... and... and... aaaaaaaand... nothing
happens. It's "normalized" to "x xor ⟨⟩".
What's going on? After all, the second clause says that, by definition,
"x xor ⟨⟩" is equal to x. That's definitional equality! Go use it, Agda!
Why would you use the first clause's definitional equality for
reduction, but not the second clause's?
--- Start of undergraduate handwaving. Somebody please speak up, if I'm
spreading fake news here. ---
For efficient reduction of applications of your _xor_ function, Agda
builds a decision tree based on your pattern matches. When reducing an
application, Agda consults this decision tree to figure out which of the
clauses to pick for the arguments given in the application. At each
level of the tree, one of the arguments is consulted to decide which
branch to take to the next level of the tree. When Agda hits a leaf,
this leaf tells her which clause to pick.
In your case, the first level of the decision tree corresponds to the
first argument. Is it ⟨⟩? Or is it O? Or I? In the first normalization
above, the one about "⟨⟩ xor y", this question can be answered: The
first argument is ⟨⟩. So Agda can proceed to the second level of the
decision tree, i.e., to the second argument. Progress!
In the second normalization above, however, Agda cannot answer this
question. The first argument is x. Could be anything. Agda thus doesn't
know which branch of the decision tree to take at the first level, so
she can't proceed to the next level, i.e., to the second argument. End
of story. No reduction to x for us. Too bad, so sad.
--- End of undergraduate handwaving. ---
I typically encounter this when it impacts propositional equality in
proofs and my ability to refl my way out of a proof. Let's consider
again the first of the above normalizations. I can refl my way out of
this:
proof₁ : ∀ y → ⟨⟩ xor y ≡ y
proof₁ y = refl
Why does this work? Well, as seen above, "⟨⟩ xor y" reduces (normalizes)
to y, so we have "y ≡ y", which is refl.
In contrast, I cannot refl my way out of this:
proof₂ : ∀ x → x xor ⟨⟩ ≡ x
proof₂ x = refl -- does not type-check
As seen in the second "normalization" above, "x xor ⟨⟩" doesn't reduce.
So, we are stuck with "x xor ⟨⟩ ≡ x", which isn't refl.
What to do about it? In order to make it past the first level of the
decision tree, we need to give Agda as the first argument a concrete
data constructor for Bin to work with, instead of just x. Based on this
data constructor Agda can then pick which branch to take from the first
to the second level.
But which of the constructors do we use in lieu of x? Well, as x can be
anything, the answer is: all of them. This is accomplished by a case
split on x. Let's ask Agda's Emacs mode to do this for us:
proof₂ ⟨⟩ = { }2
proof₂ (x O) = { }3
proof₂ (x I) = { }4
Now we can refl out way out of this again:
proof₂ ⟨⟩ = refl
proof₂ (x O) = refl
proof₂ (x I) = refl
While the above scenarios are easy to spot, this can also bite you in
proofs where a function has a larger number of arguments. I find that I
sometimes unexpectedly have to case-split on an argument, because that's
what the decision tree wants, even though my proof doesn't require the
resulting cases of the argument to be considered separately. I then put
the proof shared between the resulting cases in a lemma and use that
lemma to prove the cases in one simple step per case, just like the
above three refls.
All in all, if a function application doesn't reduce, then you need to
figure out what the decision tree really wants from you.
If you prefer a more rigorous answer to all of this, then there's the
following paper by Jesper Cockx and Andreas Abel, which explains
elaboration from clauses to a case tree in detail:
https://www.cambridge.org/core/journals/journal-of-functional-programming/article/elaborating-dependent-copattern-matching-no-pattern-left-behind/F13CECDAB2B6200135D45452CA44A8B3
So, that's how definitional equality seems to kind of break down
sometimes. Thence the highlighting.
I hope all of this helps to provide some additional context.
Thomas
On 2020-09-29 16:01, David Banas wrote:
> Hi all,
>
> I'm wondering why the second line in my definition for `XOR` below is
> being partially highlighted in a very light gray background, as shown.
>
> Thanks,
> -db
>
> data Bin : Set where
> ⟨⟩ : Bin
> _O : Bin → Bin
> _I : Bin → Bin
>
> _xor_ : Bin → Bin → Bin
> ⟨⟩ xor y = y
> x xor ⟨⟩ = x
> (x O) xor (y O) = (x xor y) O
> (x O) xor (y I) = (x xor y) I
> (x I) xor (y O) = (x xor y) I
> (x I) xor (y I) = (x xor y) O
> _______________________________________________
> Agda mailing list
> Agda at lists.chalmers.se
> https://lists.chalmers.se/mailman/listinfo/agda
More information about the Agda
mailing list