Two terms in agda are said to be definitionally equal precisely when they both have the same normal form ---I think---, and propositional equality is just the data-type representation of definitional equality ---again, I guess---; so then shouldn't propositionally equality be decidable? That is, would it seem reasonable that we can write a function typed
∀{A : Set} → (x y : A) → Dec(x ≡ y).
I kinda of get that we cannot write such a function since we cannot pattern match on the arguments, but I 'feel' that it should be possible: again, just reduce to normal form and check for syntactic identity.
Any insight would be helpful!
Two terms in agda are said to be definitionally equal precisely when
they both have the same normal form
Up to αη-conversion.
and propositional equality is just the data-type representation of definitional equality
Propositional equality says "these two terms will become definitionally equal after you instantiate some free variables" ("some" can be 0 or all of them) ("instantiate" can vary too).
E.g.
double-double : (n : ℕ) -> n + n ≡ 2 * n
clearly, n + n is not syntactically equal to 2 * n, but for any canonical n (0, 1, 2...) the result of n + n is syntactically equal to the result of 2 * n — that's what double-double says. And that "for any canonical n" part forces us to prove double-double by induction (though, in a more complicated system, where definitional equality is based on supercompilation or there is a build-in prover, n + n is definitionally equal to 2 * n).
But sometimes it's not as obvious how an induction hypothesis should look like, e.g. when you need to generalize an equation. As you might expect, there is no decision procedure for "is this arbitrary thing provable?" and hence propositional equality is undecidable. Moreover, you can't neither prove nor disprove statements like
(λ n -> 1 + n) ≡ (λ n -> n + 1)
without additional postulates.
However you really can check for syntactic equality:
_≟_ : ∀ {α} {A : Set α} -> (x y : A) -> Maybe (x ≡ y)
It says "if two terms are syntactically equal, then they are equal propositionally, otherwise we don't know whether they are equal propositionally or not". But Agda doesn't have such built-in function.
Related
Is there already some term equality relation defined in Isabelle? What is the broadest set of terms on which it is defined?
Just to be clear, I'm looking for a relation a ~ b that returns True iff a is b in the sense that they look exactly the same written down: no properties of a and b should need to be known to evaluate a ~ b.
No such thing exists. There's no notion of syntactic equality in the logic, because it admits definitions (x ≡ T) and substitution (x ≡ y ⇒ P x ≡ P y).
What you could do instead is use ML's equality operator on terms. But for that, you have to use ML blocks.
As a beginner in type-driven programming, I'm curious about the use of the == operator. Examples demonstrate that it's not sufficient to prove equality between two values of a certain type, and special equality checking types are introduced for the particular data types. In that case, where is == useful at all?
(==) (as the single constituent function of the Eq interface) is a function from a type T to Bool, and is good for equational reasoning. Whereas x = y (where x : T and y : T) AKA "intensional equality" is itself a type and therefore a proposition. You can and often will want to bounce back and forth between the two different ways of expressing equality for a particular type.
x == y = True is also a proposition, and is often an intermediate step between reasoning about (==) and reasoning about =.
The exact relationship between the two types of equality is rather complex, and you can read https://github.com/pdorrell/learning-idris/blob/9d3454a77f6e21cd476bd17c0bfd2a8a41f382b7/finished/EqFromEquality.idr for my own attempt to understand some aspects of it. (One thing to note is that even though an inductively defined type will have decideable intensional equality, you still have to go through a few hoops to prove that, and a few more hoops to define a corresponding implementation of Eq.)
One particular handy code snippet is this:
-- for rel x y, provide both the computed value, and the proposition that it is equal to the value (as a dependent pair)
has_value_dpair : (rel : t -> t -> Bool) -> (x : t) -> (y : t) -> (value: Bool ** rel x y = value)
has_value_dpair rel x y = (rel x y ** Refl)
You can use it with the with construct when you have a value returned from rel x y and you want to reason about the proposition rel x y = True or rel x y = False (and rel is some function that might represent a notion of equality between x and y).
(In this answer I assume the case where (==) corresponds to =, but you are entirely free to define a (==) function that doesn't correspond to =, eg when defining a Setoid. So that's another reason to use (==) instead of =.)
You still need good old equality because sometimes you can't prove things. Sometimes you don't even need to prove. Consider next example:
countEquals : Eq a => a -> List a -> Nat
countEquals x = length . filter (== x)
You might want to just count number of equal elements to show some statistics to user. Another example: tests. Yes, even with strong type system and dependent types you might want to perform good old unit tests. So you want to check for expectations and this is rather convenient to do with (==) operator.
I'm not going to write full list of cases where you might need (==). Equality operator is not enough for proving but you don't always need proofs.
Lean comes with a decidable_linear_order typeclass containing useful lemmas about an ordering and its relation to equality, such as:
lemma eq_or_lt_of_not_lt [decidable_linear_order α] {a b : α} (h : ¬ a < b) : a = b ∨ b < a
The equalities in these orderings are all expressed in terms of =:
inductive eq {α : Sort u} (a : α) : α → Prop
| refl : eq a
I was wondering whether it would be possible to somehow extend this class (and its superclasses) to work with an arbitrary used defined equality relation R: α → α → Prop that was reflexive, symmetric and transitive, or would this only be possible by rewriting all the relevant lemmas and their proofs to use R instead of eq?
Since these classes are not parameterized by the equality relation, you would indeed have to reimplement them (perhaps metaprogramming may be of help for that). Alternatively, because you have an equivalence relation, you could define your order on the quotient type and so keep using eq.
I have been trying to understand the applied lambda calculus. Up till now, I have understood how type inference works. But I am not able to follow what is the meaning of saying that a term is well-typed or ill-typed and then how can I determine whether a given term is well-typed or ill-typed.
For example, consider a lambda term tw defined as λx[(x x)] . How to conclude whether it is a well-typed or ill-typed term?
If we are talking about Simply Typed Lambda Calculus with some additional constants and basic types (i.e. applied lambda calculus), then the term λx:σ. (x x) is well-formed, but ill-typed.
'Well-formed' means syntactically correct, i.e. will be accepted by a parser for STLC. 'Ill-typed' means the type-checker would not pass it further.
Type-checker works according to the typing rules, which are usually expressed as a number of typing judgements (one typing scheme for each syntactic form).
Let me show that the term you provided is indeed ill-typed.
According to the rule (3) [see the typing rules link], λx:σ. (x x) must have type of general form σ -> τ (since it is a function, or more correctly abstraction). But that means the body (x x) must have some type τ (assuming x : σ). This is basically the same rule (3) expressed in a natural language. So, now we need to figure out the type of the function's body, which is an application.
Now, the rule for application (4) says that if we have an expression like this (e1 e2), then e1 must be some function e1 : α -> β and e2 : α must be an argument of the right type. Let's apply this rule to our expression for the body (x x). (1) x : α -> β and (2) x : α. Since an term in STLC can have only one type, we've got an equation: α -> β = α.
But there is no way we can unify both types together, since α is a subpart of α -> β. That's why this won't typecheck.
By the way, one of the major points of STLC was to forbid self-application (like (x x)), because it prevents from using (untyped) lambda calculus as a logic, since one can perform non-terminating calculations using self-application (see for instance Y-combinator).
I've been experimenting with Idris and it seems like it should be simple to specify some sort of type for representing all numbers between two different numbers, e.g. NumRange 5 10 is the type of all numbers between 5 and 10. I'd like to include doubles/floats, but a type for doing the same with integers would be equally useful. How would I go about doing this?
In practice, you may do better to simply check the bounds as needed, but you can certainly write a data type to enforce such a property.
One straightforward way to do it is like this:
data Range : Ord a => a -> a -> Type where
MkRange : Ord a => (x,y,z : a) -> (x >= y && (x <= z) = True) -> Range y z
I've written it generically over the Ord typeclass, though you may need to specialize it. The range requirement is expressed as an equation, so you simply supply a Refl when constructing it, and the property will then be checked. For example: MkRange 3 0 10 Refl : Range 0 10. One disadvantage of something like this is the inconvenience of having to extract the contained value. And of course if you want to construct an instance programmatically you'll need to supply the proofs that the bounds are indeed satisfied, or else do it in some context that allows for failure, like Maybe.
We can write a more elegant example for Nats without much trouble, since for them we already have a library data type to represent comparison proofs. In particular LTE, representing less-than-or-equal-to.
data InRange : Nat -> Nat -> Type where
IsInRange : (x : Nat) -> LTE n x -> LTE x m -> InRange n m
Now this data type nicely encapsulates a proof that n ≤ x ≤ m. It would be overkill for many casual applications, but it certainly shows how you might use dependent types for this purpose.