Why would adding a definition change type-correctness of a locale import? - type-inference

Consider this example—note that F1 and F2 are identical.
theory Scratch
imports Main
begin
locale F0 =
fixes meaning :: ‹'model ⇒ 'a set› ("⟦_⟧")
locale F1 = F0 +
fixes γ :: ‹'a set ⇒ 'model›
assumes sound: ‹L ⊆ ⟦γ L⟧›
(* Typechecks. *)
definition (in F0) "models m L ≡ L ⊆ ⟦m⟧"
locale F2 = F0 +
fixes γ :: ‹'a set ⇒ 'model›
assumes sound: ‹L ⊆ ⟦γ L⟧›
(* Does not typecheck, see below. *)
end
The locale F2—which is the same as the well-typed F1 except we've added a definition to F0—fails type-checking with the error message:
Type unification failed
Type error in application: incompatible operand type
Operator: meaning :: 'a ⇒ 'b set
Operand: γ L :: 'model
Apparently, when type-checking F2, does the type checker suddenly decide that the free type variables 'a and 'model cannot be the same thing?

Isabelle tools have a tendency to ‘normalize’ the names of type variables everywhere, including locales. When they do this, all type variables get replaced by 'a, 'b, 'c, etc left-to-right. Apparently, the definition command somehow triggers this. Therefore, the 'a and 'model in F0 suddenly become 'b and 'a.
If you want to override this, you can respecify the type variables explicitly:
locale F2 = F0 meaning
for meaning :: "'model ⇒ 'a set" +
fixes γ :: ‹'a set ⇒ 'model›
assumes sound: ‹L ⊆ meaning (γ L)›
or
locale F2 = F0 +
constrains meaning :: "'model ⇒ 'a set"
fixes γ :: ‹'a set ⇒ 'model›
assumes sound: ‹L ⊆ meaning (γ L)›

Related

Existential goals are filled in too soon

I have a Class containing both data and axioms. I want to build another instance in proof mode, based on (1) an existing instance and (2) some other input. I want to destruct this second input before creating the new instance of the record.
The minimal Class that works as an example is shrunk from one in jwiegley/category-theory:
Require Import Coq.Unicode.Utf8.
Require Import Coq.Init.Datatypes.
Require Import Coq.Classes.Morphisms.
Require Import Coq.Classes.SetoidDec.
Generalizable All Variables.
Reserved Infix "~>" (at level 90, right associativity).
Reserved Infix "∘" (at level 40, left associativity).
Record Category := {
obj : Type;
uhom := Type : Type;
hom : obj -> obj -> uhom where "a ~> b" := (hom a b);
homset :> ∀ X Y, Setoid (X ~> Y);
compose {x y z} (f: y ~> z) (g : x ~> y) : x ~> z
where "f ∘ g" := (compose f g);
compose_respects x y z :>
Proper (equiv ==> equiv ==> equiv) (#compose x y z);
}.
Suppose (2) is bool:
Definition newCat (C : Category) (b : bool) : Category.
Proof.
destruct b.
- eapply Build_Category.
Unshelve.
At this point, obj is filled in with Type:
C : Category
============================
∀ x y z : Type, Proper (equiv ==> equiv ==> equiv) (?compose x y z)
subgoal 2 (ID 18) is:
∀ x y z : Type, (λ _ A : Type, A) y z → (λ _ A : Type, A) x y → (λ _ A : Type, A) x z
This behavior disappears if I remove the compose_respects axiom (or use some other kind of Record without such a field). If I change Category into a Class, obj will be filled in as the obj of C. It seems to have something to do with typeclass resolution (the fact that the equivs have implicit typeclass arguments?).
Is there someway to prevent these (or any!) variables from being filled in with unification? The optimal result would be something like eapply+Unshelve where no existentials are generated at all, and I can fill in the record's fields as subgoals, in order.
It looks like simple notypeclasses refine {| obj := _ |} does the trick.
{| obj := _|} is record syntax that functions as shorthand for Build_Category _ _ _ _ _.
simple notypeclasses refine is all one tactic. It's a variant of notypeclasses refine that doesn't shelve goals and performs no reduction.
Sadly there isn't a generic notypeclasses combinator, unlike unshelve. There's just notypeclasses refine and simple notypeclasses refine.
For debugging, you can use the (undocumented) Set Typeclasses Debug. This reveals that eapply Build_Category does resolve some typeclasses, and refine {| obj := _|} is even worse.
As an aside, I don't think it makes sense to have Class Category without any type-level parameters - why would you ever want just any category automatically inferred?

Using sets in lean

I'd like to do some work in topology using lean.
As a good start, I wanted to prove a couple of simple lemmas about sets in lean.
For example
def inter_to_union (H : a ∈ set.inter A B) : a ∈ set.union A B :=
sorry
or
def set_deMorgan : a ∈ set.inter A B → a ∈ set.compl (set.union (set.compl A) (set.compl B)) :=
sorry
or, perhaps more interestingly
def set_deMorgan2 : set.inter A B = set.compl (set.union (set.compl A) (set.compl B)) :=
sorry
But I can't find anywhere elimination rules for set.union or set.inter, so I just don't know how to work with them.
How do I prove the lemmas?
Also, looking at the definition of sets in lean, I can see bits of syntax, which look very much like paper maths, but I don't understand at the level of dependent type theory, for example:
protected def sep (p : α → Prop) (s : set α) : set α :=
{a | a ∈ s ∧ p a}
How can one break down the above example into simpler notions of dependent/inductive types?
That module identifies sets with predicates on some type α (α is usually called 'the universe'):
def set (α : Type u) := α → Prop
If you have a set s : set α and for some x : α you can prove s a, this is interpreted as 'x belongs to s'.
In this case, x ∈ A is a notation (let us not mind about typeclasses for now) for set.mem x A which is defined as follows:
protected def mem (a : α) (s : set α) :=
s a
The above explains why the empty set is represented as the predicate always returning false:
instance : has_emptyc (set α) :=
⟨λ a, false⟩
And also, the universe is unsurprisingly represented like so:
def univ : set α :=
λ a, true
I'll show how prove the first lemma:
def inter_to_union {α : Type} {A B : set α} {a : α} : A ∩ B ⊆ A ∪ B :=
assume (x : α) (xinAB : x ∈ A ∩ B), -- unfold the definition of `subset`
have xinA : x ∈ A, from and.left xinAB,
#or.inl _ (x ∈ B) xinA
This is all like the usual "pointful" proofs for these properties in basic set theory.
Regarding your question about sep -- you can see through notations like so:
set_option pp.notation false
#print set.sep
Here is the output:
protected def set.sep : Π {α : Type u}, (α → Prop) → set α → set α :=
λ {α : Type u} (p : α → Prop) (s : set α),
set_of (λ (a : α), and (has_mem.mem a s) (p a))

How to prove a relation at compile-time in Lean?

Say I have a type:
inductive is_sorted {α: Type} [decidable_linear_order α] : list α -> Prop
| is_sorted_zero : is_sorted []
| is_sorted_one : Π (x: α), is_sorted [x]
| is_sorted_many : Π {x y: α} {ys: list α}, x < y -> is_sorted (y::ys) -> is_sorted (x::y::ys)
And it's decidable:
instance decidable_sorted {α: Type} [decidable_linear_order α] : ∀ (l : list α), decidable (is_sorted l)
If I have a particular list:
def l1: list ℕ := [2,3,4,5,16,66]
Is it possible to prove that it's sorted at "compile time"; to produce an is_sorted l1 at the top level?
I've tried def l1_sorted: is_sorted l1 := if H: is_sorted l1 then H else sorry, but I don't know how to show the latter case is impossible. I've also tried the simp tactic but that didn't seem to help.
I can prove it with #reduce, but it doesn't possible to assign the output of that to a variable.
You should be able to use dec_trivial to prove l1_sorted. This will try to infer an instance of decidable (is_sorted l1), and if the instance evaluates to is_true p, it will reduce to p.

Why does Idris' Refl sometimes not type-check?

I'm working through the Idris book, and I'm doing the first exercises on proof.
With the exercise to prove same_lists, I'm able to implement it like this, as matching Refl forces x and y to unify:
total same_lists : {xs : List a} -> {ys : List a} ->
x = y -> xs = ys -> x :: xs = y :: ys
same_lists Refl Refl = Refl
However, when I try to prove something else in the same manner, I get mismatches. For example:
total allSame2 : (x, y : Nat) -> x = y -> S x = S y
allSame2 x y Refl = Refl
The compiler says:
Type mismatch between
y = y (Type of Refl)
and
x = y (Expected type)
If I case-match after the =, either explicitly or with a lambda, it works as expected:
total allSame2 : (x : Nat) -> (y : Nat) -> x = y -> S x = S y
allSame2 x y = \Refl => Refl
What's the difference here?
Another modification that works is making the problematic arguments implicit:
total allSame2 : {x : Nat} -> {y : Nat} -> x = y -> S x = S y
allSame2 Refl = Refl
I do not know all the details, but I can give you a rough idea. In Idris, the parameter lists of named functions are special in that it is part of dependent pattern matching. When you pattern match it also rewrites the other parameters.
same_lists x y Refl = Refl is not valid, I roughly guess, because Idris is rewriting x and y to be the same, and you are not allowed to then give different names to this single value — I hope someone can give a better explanation of this mechanism. Instead you may use same_lists x x Refl = Refl — and note that the name x is not important, just that the same name is used in both sites.
A lambda parameter is apart from the named parameter list. Therefore, since you are doing the matching in the lambda, Idris is only going to rewrite the other parameters at that point. The key is that with the first example Idris wants to do it all at once because it is part of the same parameter list.
With the final example the only change is that you did not give distinct names to the parameters. It would have also been valid to use all_same _ _ Refl = Refl. When the parameters are implicit, Idris will fill them in correctly for you.
Finally you can consider same_lists = \x, y, Refl => Refl which also works. This is because Idris does not rewrite in unnamed parameter lists (i.e. lambda parameters).

refl in agda : explaining congruence property

With the following definition of equality, we have refl as constructor
data _≡_ {a} {A : Set a} (x : A) : A → Set a where
refl : x ≡ x
and we can prove that function are congruent on equality
cong : ∀ { a b} { A : Set a } { B : Set b }
(f : A → B ) {m n} → m ≡ n → f m ≡ f n
cong f refl = refl
I am not sure I can parse what is going on exactly here.
I think we are pattern matching refl on hidden parameters : if we replace the first occurence by refl by another identifier, we get a type error.
after pattern matching, I imagine that m and n are the same by the definition of refl. then magic occurs (a definition of functionality of a relation is applied ? or is it build in ?)
Is there an intuitive description on what is going on ?
Yes, the arguments in curly braces {} are implicit and they only need to be supplied or matched if agda cannot figure them out. It is necessary to specify them, since dependent types needs to refer to the values they depend on, but dragging them around all the time would make the code rather clunky.
The expression cong f refl = refl matches the explicit arguments (A → B) and (m ≡ n). If you wanted to match the implicit arguments, you'd need to put the matching expression in {}, but here there is no need for that. Then on the right hand side it is indeed the construction of (f m ≡ f n) using refl, and it works "by magic". Agda has a built-in axiom that proves this to be true. That axiom is similar (but stronger than) J-axiom - the induction axiom: if something C : (x y : A) → (x ≡ y) → Set is true for C x x refl, then it is also true for any x y : A and p : x ≡ y.
J : forall {A : Set} {C : (x y : A) → (x ≡ y) → Set} →
(c : ∀ x → C x x refl) →
(x y : A) → (p : x ≡ y) → C x y p
-- this really is an axiom, but in Agda there is a stronger built-in,
-- which can be used to prove this
J c x .x refl = c x -- this _looks_ to only mean x ≡ x
-- but Agda's built-in extends this proof to all cases
-- for which x ≡ y can be constructed - that's the point
-- of having induction
cong : ∀ { a b} { A : Set a } { B : Set b }
(f : A → B ) {m n} → m ≡ n → f m ≡ f n
cong f {x} {y} p = J {C = \x y p → f x ≡ f y} -- the type of equality
-- of function results
(\_ → refl) -- f x ≡ f x is true indeed
x y p
(In this last line we: match explicit arguments f and p, and also the implicit arguments m=x and n=y. Then we pass to J one implicit argument, but it is not the first positional implicit, so we tell agda that it is C in the definition - without doing that, Agda won't see what type is meant by refl in \_ → refl)