[Agda] proofs with "with"
Peter Selinger
selinger at mathstat.dal.ca
Tue Dec 16 04:03:09 CET 2014
Thanks to all who replied.
Liam O'Connor wrote:
>
> You probably want to use the “inspect” idiom.
>
> See here:
>
> https://github.com/agda/agda-stdlib/blob/master/src/Relation/Binary/PropositionalEquality.agda
>
> Under “inspect on steroids”
Yes, that is exactly the kind of thing I was looking for.
Wolfram Kahl wrote:
> The "with" notation just generates an auxiliary function;
> it is usually only a minor nuisance to generate that function
> yourself.
Point well taken. Although writing the auxiliary function by hand does
not entirely make the problem go away. In my example, if I write
aux : (L : List Nat) -> ∃[ n ∈ Nat ] (n ∉ L) -> newNat L ∉ L
aux L [ n , p ] = ?
lemma-newNat : (L : List Nat) -> newNat L ∉ L
lemma-newNat L = aux L (theorem-infinite L)
then in the context "?", I still end up with the hypothesis n ∉ L and
the goal newNat L ∉ L, and no way to prove it. So I must pass the
required equality as an additional argument to aux, like this:
aux : (L : List Nat) -> (x : ∃[ n ∈ Nat ] (n ∉ L)) -> x == theorem-infinite L -> newNat L ∉ L
aux L [ n , p ] eq = ?
lemma-newNat : (L : List Nat) -> newNat L ∉ L
lemma-newNat L = aux L (theorem-infinite L) refl
At this point, the solution is essentially equivalent to what
"inspect" does, and arguably the auxiliary function has not increased
transparency.
-- Peter
> On 15 December 2014 at 4:28:07 pm, Peter Selinger (selinger at mathstat.dal.ca) wrote:
>
> This is a newbie question. I am being driven mad by proofs about
> definitions that use the "with" notation. The situation is typically
> like this:
>
> lemma : A -> B
> lemma x with expr
> lemma x | y = ?
>
> Within the proof context '?', a variable y is available, of the same
> type as expr. However, often, to complete the proof, one has to use
> the fact that y == expr. This fact is obviously true, but typically
> does not follow from anything in the proof context.
>
> The "rewrite" notation does not help here, because its purpose is to
> use an equation, not to prove one.
>
> Here is a concrete example (full definitions and proofs appended
> below). I have defined an operation for computing the maximum of a
> list of natural numbers:
>
> maximum : (L : List Nat) -> Nat
>
> I have also proved a lemma showing that the maximum is indeed an upper
> bound for the list:
>
> lemma-maximum : (L : List Nat) -> ∀ m -> m ∈ L -> m <= maximum L
>
> One can then easily prove a theorem stating that there are infinitely
> many natural numbers:
>
> theorem-infinite : (L : List Nat) -> ∃[ n ∈ Nat ] (n ∉ L)
>
> Since existential witnesses are not very convenient to use, I'd like
> to split this theorem into a first and second component: the first
> component is a function computing a natural number not in a given
> list, and the second component is the lemma saying that the number is
> indeed not in the list:
>
> -- Return a natural number not in L.
> newNat : (L : List Nat) -> Nat
> newNat L with theorem-infinite L
> newNat L | ([ n , p ]) = n
>
> -- The defining property of newNat.
> lemma-newNat : (L : List Nat) -> newNat L ∉ L
> lemma-newNat L with theorem-infinite L
> lemma-newNat L | ([ n , p ]) = ?
>
> However, I cannot complete the last proof. In principle, the question
> mark should just be "p". However, the prover (modulo beta reduction)
> serves
>
> Goal: newNat L ∉ L
>
> as the goal, and gives
>
> p : n ∉ L
> n : Nat
> L : List Nat
>
> as the hypotheses. Obviously, [ n , p ] was obtained by pattern
> matching theorem-infinite L, so we know that
>
> [ n , p ] == theorem-infinite L.
>
> >From this, we could immediately conclude that newNat L == n, which
> would immediately prove the goal. But I have not been able to find any
> combination of dot-patterns that the type checker would actually
> accept.
>
> Two questions:
>
> (1) what is the concrete solution to my problem?
>
> I realize that I can solve the problem by defining general first
> and second projections from the existential type:
>
> ∃fst : {A : Set} {B : A -> Set} -> ∃[ x ∈ A ] B x -> A
> ∃snd : {A : Set} {B : A -> Set} -> (p : ∃[ x ∈ A ] B x) -> B (∃fst p)
>
> and then I can just define newNat and lemma-newNat in terms of
> these. However, I am more concerned with what one can do when one
> is stuck in a proof context, rather than such an "external"
> solution to the problem.
>
> (2) why doesn't Agda do the obvious, namely, when matching a "with"
> expression "expr" against a pattern "p", just add the equation
> "expr == p" to the proof context? Is there an existing mechanism
> that can achieve this?
>
> Thanks, -- Peter
>
> Here's the full example. It requires no libraries.
>
> module InfNat where
>
> -- Falsity.
> data False : Set where
> -- no constructors.
>
> -- Negation.
> Not : Set -> Set
> Not A = A -> False
>
> -- Existential quantifier.
> data Exists (A : Set) (B : A -> Set) : Set where
> [_,_] : (a : A) -> B a -> Exists A B
> syntax Exists A (λ x -> B) = ∃[ x ∈ A ] B
>
> -- The natural numbers.
> data Nat : Set where
> zero : Nat
> succ : Nat -> Nat
>
> -- Order.
> data _<=_ : Nat -> Nat -> Set where
> z<=n : ∀ {n} → zero <= n
> s<=s : ∀ {m n} (m<=n : m <= n) → succ m <= succ n
>
> -- Order is reflexive.
> lemma-order-refl : ∀ {n} -> n <= n
> lemma-order-refl {zero} = z<=n
> lemma-order-refl {succ n} = s<=s lemma-order-refl
>
> -- Order is transitive.
> lemma-order-trans : ∀ {n m k} -> n <= m -> m <= k -> n <= k
> lemma-order-trans z<=n q = z<=n
> lemma-order-trans (s<=s p) (s<=s q) = s<=s (lemma-order-trans p q)
>
> -- Successor is not smaller.
> lemma-order-succ : ∀ {n} -> Not (succ n <= n)
> lemma-order-succ (s<=s h) = lemma-order-succ h
>
> -- The maximum of two numbers.
> max : Nat -> Nat -> Nat
> max zero m = m
> max n zero = n
> max (succ n) (succ m) = succ (max n m)
>
> -- Introduction rules for maximum.
> lemma-max-intro1 : ∀ n m -> n <= max n m
> lemma-max-intro1 zero m = z<=n
> lemma-max-intro1 (succ n) zero = lemma-order-refl
> lemma-max-intro1 (succ n) (succ m) = s<=s (lemma-max-intro1 n m)
>
> lemma-max-intro2 : ∀ n m -> m <= max n m
> lemma-max-intro2 zero m = lemma-order-refl
> lemma-max-intro2 (succ n) zero = z<=n
> lemma-max-intro2 (succ n) (succ m) = s<=s (lemma-max-intro2 n m)
>
> -- Lists.
> data List (A : Set) : Set where
> [] : List A
> _::_ : A -> List A -> List A
>
> -- Membership.
> data _∈_ {A : Set} (x : A) : (B : List A) -> Set where
> mem-head : {xs : List A} -> x ∈ (x :: xs)
> mem-tail : {xs : List A} {y : A} -> x ∈ xs -> x ∈ (y :: xs)
>
> -- Not member.
> _∉_ : {A : Set} -> (x : A) -> (B : List A) -> Set
> x ∉ B = Not (x ∈ B)
>
> -- Calculate the maximum of a list of numbers.
> maximum : (L : List Nat) -> Nat
> maximum [] = zero
> maximum (n :: L) = max n (maximum L)
>
> -- The maximum is an upper bound.
> lemma-maximum : (L : List Nat) -> ∀ m -> m ∈ L -> m <= maximum L
> lemma-maximum (n :: L) .n mem-head = lemma-max-intro1 n (maximum L)
> lemma-maximum (n :: L) m (mem-tail p) = lemma-order-trans (lemma-maximum L m p) (lemma-max-intro2 n (maximum L))
>
> -- Theorem: there are infinitely many natural numbers.
> theorem-infinite : (L : List Nat) -> ∃[ n ∈ Nat ] (n ∉ L)
> theorem-infinite L = [ n , p ]
> where
> n = succ (maximum L)
> p : n ∉ L
> p h = let q = lemma-maximum L n h in lemma-order-succ q
>
> -- Return a natural number not in L.
> newNat : (L : List Nat) -> Nat
> newNat L with theorem-infinite L
> newNat L | ([ n , p ]) = n
>
> -- The defining property of newNat.
> lemma-newNat : (L : List Nat) -> newNat L ∉ L
> lemma-newNat L with theorem-infinite L
> lemma-newNat L | ([ n , p ]) = {!!}
More information about the Agda
mailing list