agda / agda

Agda is a dependently typed programming language / interactive theorem prover.
https://wiki.portal.chalmers.se/agda/pmwiki.php
Other
2.41k stars 339 forks source link

Termination checking prefers `data` to `record` #7206

Closed lawcho closed 3 months ago

lawcho commented 3 months ago

foldSS passes when data is used, but not when record is used:

-- Fails syntax-based termination checking
record SillyStream (A : Set) : Set where
  inductive; eta-equality; constructor _∷_
  field head : A
  field tail : SillyStream A

-- -- Passes syntax-based termination checking
-- data SillyStream (A : Set) : Set where
--   _∷_
--     : (head : A)
--     → SillyStream A
--     → SillyStream A

foldSS : {A r : Set} → (A → r → r) → SillyStream A → r
foldSS f (head ∷ tail)  = f head (foldSS f tail)

I expected syntax-based termination to spot the structural recursion either way.

Agda Versions tested

andreasabel commented 3 months ago

It should work if you put no-eta-equality.
With eta, foldSS is not terminating due to the record pattern translation which turns this into

foldSS f s = f (head s) (foldSS f (tail s))
lawcho commented 3 months ago

This passes:

record SillyStream (A : Set) : Set where
  inductive; pattern; constructor _∷_
  field head : A
  field tail : SillyStream A

foldSS : {A r : Set} → (A → r → r) → SillyStream A → r
foldSS f (head ∷ tail)  = f head (foldSS f tail)
andreasabel commented 3 months ago

Yes, because eta is off. This gives an error:

...
module _ (A : Set) (s : SillyStream A) where

  eta : s ≡ head s ∷ tail s
  eta = refl
lawcho commented 3 months ago

It seems like pattern is enough to get data-style checking of a record.

I don't think a doc update is required, since the pattern keyword is discoverable via Agda's error message:

/tmp/tmp.O6kGsXwZPr/tmp.agda:8,11-22
Pattern matching on no-eta record type SillyStream
(defined at /tmp/tmp.O6kGsXwZPr/tmp.agda:2,8-19) is not allowed
(to activate, add declaration `pattern` to record definition)
when checking that the pattern head ∷ tail has type SillyStream A

which arises when I give a naive translation of the data type:

record SillyStream (A : Set) : Set where
  inductive; constructor _∷_
  field head : A
  field tail : SillyStream A

(Agda version 422b932d of master)