I am trying to understand how to create a working "if and only if" statement in agda but have problems in proving the false cases for it, also in using induction in the proof. As an example I want to work with "less than or equal", which I define as:
data ℕ : Set where
zero : ℕ
succ : ℕ → ℕ
data _leq_ : ℕ → ℕ → Set where
0≤n : ∀ {n : ℕ} → zero leq n
Sn≤Sm : ∀ {n m : ℕ} → (n leq m) → ((succ n) leq (succ m))
To define A if and only if B, we want a pair of functions taking proofs of A to proofs of B and vice versa, so I define:
record _∧_ (A B : Set) : Set where
constructor _,_
field
fst : A
snd : B
open _∧_
_iff_ : (A B : Set) → Set
A iff B = (A → B) ∧ (B → A)
Now to my problem: I want to prove the statement that (n <= m+1) <=> (n+1 <= m+2) so formulate the following:
prop₂ : ∀ (n m : ℕ) → (n leq (succ m)) iff ((succ n) leq (succ (succ m)))
prop₂ zero zero = (λ x → Sn≤Sm 0≤n) , λ x → 0≤n
prop₂ zero (succ b) = (λ x → Sn≤Sm 0≤n) , (λ x → 0≤n)
prop₂ (succ a) zero = ?
prop₂ (succ a) (succ b) = ?
My issues are
the third line of the proof I need to give a function from an empty set to an empty set, but don't know how to formulate this
in the final line, I wish to use induction, i.e. to say prop2 (succ a) (succ b) = prop2 a b, but agda doesn't accept this as well typed?
If I stick to your development, the proof is done as follows:
prop₂ : ∀ (n m : ℕ) → (n leq (succ m)) iff ((succ n) leq (succ (succ m)))
prop₂ zero zero = (λ x → Sn≤Sm 0≤n) , λ x → 0≤n
prop₂ zero (succ b) = (λ x → Sn≤Sm 0≤n) , (λ x → 0≤n)
prop₂ (succ a) zero = Sn≤Sm , λ {(Sn≤Sm x) → x}
prop₂ (succ a) (succ b) = Sn≤Sm , λ {(Sn≤Sm x) → x}
However, there is a much better way to prove such property. As you can see, there are redondancies in the code due to the fact that you case split on the parameters a and b instead of case splitting on the proof elements about them, which contain all the information you need. This leads to the following proof, which is much more elegant and concise:
prop-better : ∀ {n m : ℕ} → (n leq (succ m)) iff ((succ n) leq (succ (succ m)))
prop-better = Sn≤Sm , λ {(Sn≤Sm x) → x}
The first direction of the equivalence is simply the Sn≤Sm constructor by definition, and the other side is done by case-splitting on the proof argument, which is necessarily of the form Sn≤Sm x since the two naturals are of the form succ y. This gives you the proof x which is exactly what you require.
I'd also like to add that this is a perfect candidate for copattern matching.
prop₂ : ∀ (n m : ℕ) → (n leq (succ m)) iff ((succ n) leq (succ (succ m)))
prop₂ n m .fst n≤m+1 = Sn≤Sm n≤m+1
prop₂ n m .snd (Sn≤Sm n≤m+1) = n≤m+1
Related
foldl : ∀ {a b} {A : Set a} (B : ℕ → Set b) {m} →
(∀ {n} → B n → A → B (suc n)) →
B zero →
Vec A m → B m
foldl b _⊕_ n [] = n
foldl b _⊕_ n (x ∷ xs) = foldl (λ n → b (suc n)) _⊕_ (n ⊕ x) xs
When translating the above function to Lean, I was shocked to find out that its true form is actually like...
def foldl : ∀ (P : ℕ → Type a) {n : nat}
(f : ∀ {n}, P n → α → P (n+1)) (s : P 0)
(l : Vec α n), P n
| P 0 f s (nil _) := s
| P (n+1) f s (cons x xs) := foldl (fun n, P (n+1)) (λ n, #f (n+1)) (#f 0 s x) xs
I find it really impressive that Agda is able to infer the implicit argument to f correctly. How is it doing that?
foldl : ∀ {a b} {A : Set a} (B : ℕ → Set b) {m} →
(∀ {n} → B n → A → B (suc n)) →
B zero →
Vec A m → B m
foldl b _⊕_ n [] = n
foldl b _⊕_ n (x ∷ xs) = foldl (λ n → b (suc n)) _⊕_ (_⊕_ {0} n x) xs
If I pass it 0 explicitly as in the Lean version, I get a hint as to the answer. What is going on is that Agda is doing the same thing as in the Lean version, namely wrapping the implicit arg so it is suc'd.
This is surprising as I thought that implicit arguments just means that Agda should provide them on its own. I did not think it would change the function when it is passed as an argument.
Add is often defined as:
add : ℕ -> ℕ -> ℕ
add zero m = m
add (suc n) m = suc (add n m)
This definition is short, but makes the proof of things like add-comm rather complex, requiring two inductive functions and calls to subst, cong and sym. If we, instead, define add as:
add : ℕ -> ℕ -> ℕ
add zero zero = zero
add (suc n) zero = suc n
add zero (suc m) = suc m
add (suc n) (suc m) = suc (suc (add n m))
Then the proof of commutativity becomes almost trivial:
add-comm : forall a b -> add a b ≡ add b a
add-comm zero zero = refl
add-comm zero (suc b) = refl
add-comm (suc a) zero = refl
add-comm (suc a) (suc b) = cong suc (cong suc (add-comm a b))
Is there any negative side to defining functions like add by listing all cases, rather than being economic?
You win some definitional equalities, but you lose others. In this case, you lose that add (suc a) b is suc (add a b) for any b. In short, if you have more cases here, you need more cases and/or proofs elsewhere, for example for Vec appending:
open import Data.Vec
add-zero : ∀ n → add zero n ≡ n
add-zero zero = refl
add-zero (suc n) = refl
suc-add : ∀ n m → suc (add n m) ≡ add (suc n) m
suc-add zero zero = refl
suc-add zero (suc m) = cong suc (cong suc (sym (add-zero m)))
suc-add (suc n) zero = refl
suc-add (suc n) (suc m) rewrite suc-add n m = refl
append : ∀ {n m}{A : Set} → Vec A n → Vec A m → Vec A (add n m)
append [] ys = subst (Vec _) (sym (add-zero _)) ys
append (x ∷ xs) ys = subst (Vec _) (suc-add _ _) (x ∷ append xs ys)
(the original add-comm doesn't require two definitions):
add : ℕ -> ℕ -> ℕ
add zero m = m
add (suc n) m = suc (add n m)
add-comm : ∀ n m → add n m ≡ add m n
add-comm zero zero = refl
add-comm zero (suc m) = cong suc (add-comm zero m)
add-comm (suc n) zero = cong suc (add-comm n zero)
add-comm (suc n) (suc m)
rewrite add-comm n (suc m)
| sym (add-comm (suc n) m)
| add-comm n m = refl
I am a noob in agda and reading http://www.cse.chalmers.se/~ulfn/papers/afp08/tutorial.pdf. My shallow knowledge somehow finds dot pattern not quite necessary. For example,
data Image_∋_ {A B : Set}(f : A → B) : B → Set where
im : (x : A) → Image f ∋ f x
inv : {A B : Set}(f : A → B)(y : B) → Image f ∋ y → A
inv f .(f x) (im x) = x
I find inv can well be defined as
inv : {A B : Set}(f : A → B)(y : B) → Image f ∋ y → A
inv _ _ (im x) = x
because from the types, we've already known y is an image of f for some x, so it cannot possibly go wrong.
Another example is
data _==_ {A : Set}(x : A) : A → Set where
refl : x == x
data _≠_ : ℕ → ℕ → Set where
z≠s : {n : ℕ} → zero ≠ suc n
s≠z : {n : ℕ} → suc n ≠ zero
s≠s : {n m : ℕ} → n ≠ m → suc n ≠ suc m
data Equal? (n m : ℕ) : Set where
eq : n == m → Equal? n m
neq : n ≠ m → Equal? n m
equal? : (n m : ℕ) → Equal? n m
equal? zero zero = eq refl
equal? zero (suc _) = neq z≠s
equal? (suc _) zero = neq s≠z
equal? (suc n') (suc m') with equal? n' m'
... | eq refl = eq refl
... | neq n'≠m' = neq (s≠s n'≠m')
consider equal? function, the second last line is written in the paper as (suc n') (suc .n') | eq refl = eq refl. Again, eq refl in with construct has provided a proof, for these two values being the same, so why do I bother writing them out using dot pattern?
I am more familiar with coq, and I am not aware of similar thing in coq. Am I missing something here?
In Coq you write the pattern-matches explicitly whereas Agda's equation-based approaches forces the typechecker to reconstruct a case-tree which ought to correspond to what you wrote.
Dotted-patterns help the typechecker see that a given pattern was not the product of a match but rather forced by a match on one of the other arguments (e.g.: a match on a Vec Bool n will force the value of n, or a match on an equality proof will, as you've observed, force some variables to be the same).
They're not always necessary and, in fact, some have been slowly made optional as you can see in the CHANGELOG for version 2.5.3:
Dot patterns.
The dot in front of an inaccessible pattern can now be skipped if the pattern consists entirely of constructors or literals. For example:
I need to define two versions of an operation with a slightly different definition. It is a series of compositions with Nat indices involved.
open import Data.Nat
data Hom : ℕ → ℕ → Set where
id : (m : ℕ) → Hom m m
_∘_ : ∀ {m n k} → Hom n k → Hom m n → Hom m k
p : (n : ℕ) → Hom (suc n) n
p1 : (m n : ℕ) → Hom (m + n) n
p1 zero n = id n
p1 (suc m) n = p1 m n ∘ p (m + n)
p2 : (m n : ℕ) → Hom (m + n) n
p2 zero n = id n
p2 (suc m) n = {!!} -- p n ∘ p2 m (1 + n)
-- Goal: Hom (suc (m + n)) n
-- Have: Hom (m + suc n) n
I would like to define both p1 and p2 and be able to use them interchangeably. Is this doable?
You can define p2 by direct recursion (no subst or rewriting) over _+_ using the trick described here. Looks like this:
record Homable (H : ℕ → ℕ → Set) : Set where
field
id-able : (m : ℕ) → H m m
_∘-able_ : ∀ {m n k} → H n k → H m n → H m k
p-able : (n : ℕ) → H (suc n) n
suc-homable : ∀ {H} → Homable H → Homable (λ m n -> H (suc m) (suc n))
suc-homable homable = record
{ id-able = λ m → id-able (suc m)
; _∘-able_ = _∘-able_
; p-able = λ m → p-able (suc m)
} where open Homable homable
p2-go : ∀ {H} → Homable H → (m : ℕ) → H m 0
p2-go homable zero = id-able 0 where
open Homable homable
p2-go homable (suc m) = p-able 0 ∘-able p2-go (suc-homable homable) m where
open Homable homable
plus-homable-hom : ∀ k → Homable (λ m n → Hom (m + k) (n + k))
plus-homable-hom k = record
{ id-able = λ n → id (n + k)
; _∘-able_ = _∘_
; p-able = λ n → p (n + k)
}
p2 : (m n : ℕ) → Hom (m + n) n
p2 m n = p2-go (plus-homable-hom n) m
The cost is that you need to maintain those Homable records which is somewhat tedious, but to my experience proving things about functions defined this way is simpler than about functions defined in terms of subst or over _+′_ (unless you never want to coerce _+′_ to _+_, of course).
Well, the value you provide has a type that is equal to the type of the hole, but Agda does not see this fact. More formally, the two types are propositionally equal but not judgementally equal. The problem is caused by the index m + suc n, which is propositionally but not judgementally equal to suc m + n because of how addition is defined. One way to solve your problem is to manually explain to Agda that the two types are equal:
open import Data.Nat
open import Data.Nat.Properties
open import Relation.Binary.PropositionalEquality
data Hom : ℕ → ℕ → Set where
id : (m : ℕ) → Hom m m
_∘_ : ∀ {m n k} → Hom n k → Hom m n → Hom m k
p : (n : ℕ) → Hom (suc n) n
p1 : (m n : ℕ) → Hom (m + n) n
p1 zero n = id n
p1 (suc m) n = p1 m n ∘ p (m + n)
p2 : (m n : ℕ) → Hom (m + n) n
p2 zero n = id n
p2 (suc m) n = subst (λ k → Hom k n) (+-suc m n) (p n ∘ p2 m (suc n))
However, this approach is not without downsides, as p2 (suc m) n is now not judgementally equal to your intended definition but to the expression above involving subst.
The problem seems essentially linked to what you're trying to do: IIUC, p1 and p2 are actually provably equal but defined using a different recursion structure. That's fine, but then the indices of your result type should follow the same recursion structure, i.e. you should define p2 using a different version of + that recurses in the appropriate way for p2:
_+′_ : ℕ → ℕ → ℕ
zero +′ n = n
suc m +′ n = m +′ suc n
p2′ : (m n : ℕ) → Hom (m +′ n) n
p2′ zero n = id n
p2′ (suc m) n = p n ∘ p2′ m (suc n)
However, this has another downside that the type of p1 and p2′ are no longer judgementally equal (but still propositionally equal though).
Another thing you can try is to use Agda's rewrite rules to give _+_ satisfy additional judgemental equalities, but this is dangerous as it may break some of Agda's desirable qualities as a logic. In this case, I suspect it's fine, but I'd have to check.
In summary, there are a number of things you can try but none is without downsides. Which is your best option depends on what you're trying to use this for.
I have a definition with the following type:
insert : ∀ {n} → (i : Fin (suc n)) → ∀ t → Env n → Env (suc n)
weaken : ∀ {t t₀ n} {Γ : Env n} → (i : Fin (suc n)) → (e : Γ ⊢ t₀) → (insert i t Γ) ⊢ t₀
Given two environments Γ : Env n and Γ′ : Env n′, and a pointer to a position in the second one, i : Fin (suc n), I would like to weaken an e : (Γ′ ++ Γ) ⊢ t₀.
In theory, this should be easy by using something like
let i′ = raise n′ i
weaken {t} i′ e : insert i′ t (Γ′ ++ Γ) ⊢ t₀
However, in practice it doesn't work out so nicely, because the typechecker is not convinced that raise n′ i has type Fin (suc _) (required by weaken):
(n′ + suc n) != (suc (_n_550 i e)) of type ℕ
when checking that the
expression i′ has type Fin (suc (_n_550 i e))
My problem is, I could use something like +-suc : ∀ n′ n → n′ + suc n ≡ suc (n′ + n) to substitute the type of i′, but then the resulting type from weaken i′ e will not have the form insert i′ t (Γ′ ++ Γ) ⊢ t₀.
Given two environments Γ : Env n and Γ′ : Env n′
Those are contexts.
It should be possible to change the type of insert to
data Bound : ℕ -> Set where
zero : ∀ {n} -> Bound n
suc : ∀ {n} -> Bound n -> Bound (suc n)
insert : ∀ {n} → (i : Bound n) → ∀ t → Env n → Env (suc n)
without changing the body of the function.
You can write a version of raise that raises under suc:
raise′ : ∀ {m} n → Fin (suc m) → Fin (suc (n + m))
raise′ zero i = i
raise′ (suc n) i = suc (raise′ n i)
But the actual solution is to rename terms using either functions:
Ren : Con -> Con -> Set
Ren Γ Δ = ∀ {σ} -> σ ∈ Γ -> σ ∈ Δ
keepʳ : ∀ {Γ Δ σ} -> Ren Γ Δ -> Ren (Γ ▻ σ) (Δ ▻ σ)
keepʳ r vz = vz
keepʳ r (vs v) = vs (r v)
ren : ∀ {Γ Δ σ} -> Ren Γ Δ -> Γ ⊢ σ -> Δ ⊢ σ
ren r (var v) = var (r v)
ren r (ƛ b ) = ƛ (ren (keepʳ r) b)
ren r (f · x) = ren r f · ren r x
or order preserving embeddings.