Irrelevant termination measure [Re: [Agda] termination checking]

Andreas Abel abela at chalmers.se
Fri Dec 19 15:40:25 CET 2014


Hi Peter,

thanks for your message.  Again, I have to disappoint you a bit, but can 
offer a workaround.

First, the bad news.  As irrelevance is definitional, termination via an 
irrelevant argument cannot be supported by the usual term model of 
dependent types:

If  x : Inductive n  appears in an irrelevant position, then we are 
allowed to replace it by any value of the same type, in particular with

   cind (cind-field x) : Inductive n

where cind-field is the inverse to cind, which is also definable for the 
data-version of Inductive.

This means that strong normalization is broken in the term model if you 
allow recursion on Inductive.

I don't know whether there are better models that can prove strong 
normalization in the presence of recursion on irrelevant arguments.

The good news is that there is a backdoor that makes your data-version 
of Inductive go through with irrelevant matching.

   {-# OPTIONS --experimental-irrelevance #-}

allows you to match irrelevantly on single-constructor data types.  Be 
warned that you are entering the wild if you use this option.  No models 
to back you up.  ;-)

Cheers,
Andreas

On 19.12.2014 03:12, Peter Selinger wrote:
> Hi Andreas,
>
> thanks for your reply. I think I understand your point. Record
> constructors are matched lazily, and therefore could lead to infinite
> loops, because a variable x of type Empty will expand to inn (inn (inn
> ...))  indefinitely.
>
> However, this is unfortunate, because it means that there is no way to
> make the "Inductive" argument irrelevant, even though it is, in fact,
> irrelevant. Or to put it differently: the combination of features of
> datatypes and record types seems to be such that it is impossible to
> prove termination by structural induction on an irrelevant argument,
> even in cases where this should be possible.
>
> I'll give a concrete example below.
>
> First I should point out the the Fibonacci example of my last email
> was a red herring. I was puzzled by the fact that the function
> fibonacci-aux passes the termination checker; however, it does so
> because the first (and not the second) argument is structurally
> decreasing. Duh. Sorry for not having noticed this earlier.
>
> Nevertheless, I will stick with the Fibonacci example, because it
> illustrates the following point better than integer division.
>
> Type-check this code:
>
> ----------------------------------------------------------------------
> open import Data.Nat
> open import Relation.Nullary
> open import Relation.Binary.PropositionalEquality
>
> module TerminationExample where
>
> -- Some true lemmas about natural numbers. Proofs omitted for brevity.
> postulate
>    lemma-refl : ∀ n -> n ≤ n
>    lemma-suc : ∀ n -> n ≤ suc n
>    lemma-lt-trans : ∀ {n m k} -> n < m -> m < k -> n < k
>    lemma-elim : ∀ {m n} {C : Set} -> m < suc n -> (m < n -> C) -> (m ≡ n -> C) -> C
>
> module Inductive-as-Record where
>
>    -- A natural number is inductive if all smaller numbers are inductive.
>    record Inductive (n : ℕ) : Set where
>      inductive
>      constructor cind
>      field cind-field : ∀ {m} -> m < n -> Inductive m
>
> module Inductive-as-Datatype where
>
>    -- The same thing defined as a datatype.
>    data Inductive : (n : ℕ) -> Set where
>      cind : ∀ {n} -> (∀ {m} -> m < n -> Inductive m) -> Inductive n
>
> open Inductive-as-Record
> -- or: open Inductive-as-Datatype
>
> -- All natural numbers are inductive. We prove it here, because the
> -- following examples don't work correctly when it is just a
> -- postulate. The details of the proof do not matter.
> lemma-inductive : ∀ n -> Inductive n
> lemma-inductive zero = cind (λ ())
> lemma-inductive (suc n) = cind (λ p → lemma-elim p case1 case2)
>    where
>    ih = lemma-inductive n
>
>    inductive-less : ∀ {n m} -> m < n -> Inductive n -> Inductive m
>    inductive-less p (cind h) = cind (λ q → h (lemma-lt-trans q p))
>
>    case1 : ∀ {m} -> m < n -> Inductive m
>    case1 q = inductive-less q ih
>
>    case2 : ∀ {m} -> m ≡ n -> Inductive m
>    case2 q = subst Inductive (sym q) ih
>
> -- The Fibonacci sequence, defined by strong recursion.
> fibonacci-aux : (n : ℕ) -> Inductive n -> ℕ
> fibonacci-aux 0 (cind h) = 1
> fibonacci-aux 1 (cind h) = 1
> fibonacci-aux (suc (suc n)) (cind h) = fibonacci-aux n (h proof1) + fibonacci-aux (suc n) (h proof2)
>    where
>    proof1 : n < suc (suc n)
>    proof1 = s≤s (lemma-suc n)
>
>    proof2 : suc n < suc (suc n)
>    proof2 = lemma-refl (suc (suc n))
>
> fibonacci : (n : ℕ) -> ℕ
> fibonacci n = fibonacci-aux n (lemma-inductive n)
>
> -- This definition is sufficient to evaluate closed terms. For
> -- example, pressing Ctrl-n here will evaluate to 89:
> test1 = {! fibonacci 10 !}
>
> -- However, partial evaluation of open terms is not possible. The
> -- following evaluates to a closure with unevaluated proof
> -- obligations. This makes sense, because (lemma-inductive n) does not
> -- evaluate to canonical form when n is an open term.
> test2 = λ n -> {! fibonacci (suc (suc n)) !}
>
> -- We can solve this problem by making the Inductive argument
> -- irrelevant:
>
> fibonacci'-aux : (n : ℕ) -> .(Inductive n) -> ℕ
> fibonacci'-aux 0 (cind h) = 1
> fibonacci'-aux 1 (cind h) = 1
> fibonacci'-aux (suc (suc n)) (cind h) = fibonacci'-aux n (h proof1) + fibonacci'-aux (suc n) (h proof2)
>    where
>    proof1 : n < suc (suc n)
>    proof1 = s≤s (lemma-suc n)
>     proof2 : suc n < suc (suc n)
>    proof2 = lemma-refl (suc (suc n))
>
> fibonacci' : (n : ℕ) -> ℕ
> fibonacci' n = fibonacci'-aux n (lemma-inductive n)
>
> -- Now both closed and open terms can be reduced:
>
> -- Term in hole evaluates to: 89
> test3 = {! fibonacci' 10 !}
>
> -- Term in hole evaluates to: fibonacci'-aux n _ + fibonacci'-aux (suc n) _
> test4 = λ n -> {! fibonacci' (suc (suc n)) !}
>
> ----------------------------------------------------------------------
>
> Note that everything works as advertised, and especially, the
> Inductive argument can be made irrelevant, as it should be.
>
> However, now change "open Inductive-as-Record" to "open
> Inductive-as-Datatype" and re-typecheck the example again.
>
> The function fibonacci'-aux now gives the error:
> "Cannot pattern match cind h against irrelevant type".
>
> So we are caught between a rock and a hard place:
>
> * If we use records, then we can make the inductive argument
>    irrelevant, but we cannot prove termination by structural recursion
>    on it. (Here, the function passes termination checking anyway, but
>    in the integer division example, it does not).
>
> * If we use datatypes, then we can prove termination on the second
>    argument, but we cannot make that argument irrelevant.
>
> As far as I can see, there is no logical reason for not being able to
> do both at the same time - it is just a nasty coincidence because each
> of the record and datatype mechanisms lack certain features, both of
> which we happen to need simultaneously here.
>
> It seems to me that solving this problem would require a (minor)
> language extension. There are several possible solutions:
>
> * add a "no-eta" option to record definitions, to turn off eta.
>    Then allow termination checking on the resulting record
>    constructors. Alternatively, as suggested by Andreas, turn off eta
>    for all inductive records, and permit termination checking on their
>    constructors.
>
> * Or: give a special status to datatype constructors when the datatype
>    has a unique constructor. Permit such constructors to be matched
>    against irrelevant arguments. (One could also permit such unique
>    constructors to be used in let-bindings, as is already permitted for
>    record constructors).
>
> Is there a solution to my problem that uses only existing language
> features? I tried many combinations of irrelevance and structural
> induction, but I did not manage to find a combination where I could
> prove termination by structural induction on an irrelevant argument.
>
> Thanks, -- Peter


-- 
Andreas Abel  <><      Du bist der geliebte Mensch.

Department of Computer Science and Engineering
Chalmers and Gothenburg University, Sweden

andreas.abel at gu.se
http://www2.tcs.ifi.lmu.de/~abel/


More information about the Agda mailing list