My proof scripts are giving me stupid type equalities like nat = bool or
nat = list unit which I need to use to solve contradictory goals.
In normal math, this would be trivial. Given sets bool := { true, false } and
nat := { 0, 1, 2, ... } I know that true ∈ bool, but true ∉ nat,
hence bool ≠ nat. In Coq, I don’t even know how to state that true :̸ nat.
Question
Is there a way to show that these equalities are false? Or maybe, is it impossible?
(Ed.: Removed long list of failed attemts, still viewable in the history.)
tl;dr Cardinality arguments are the only way to show types unequal. You can certainly automate cardinality arguments more effectively with a bit of reflection. If you want to go further, give your types a syntactic representation by constructing a universe, ensuring your proof obligations are framed as syntactic inequality of representations rather than semantic inequality of types.
Isomorphism as Equality
It’s widely believed (and there may even be a proof of it somewhere) that Coq’s logic is consistent with the axiom that isomorphic sets are propositionally equal. Indeed, this is a consequence of the Univalence Axiom from Vladimir Voevodsky, which people are having so much fun with at the moment. I must say, it seems very plausible that it is consistent (in the absence of typecase), and that a computational interpretation can be constructed which somehow transports values between equal types by inserting whichever component of the isomorphism is needed at any given moment.
If we assume that such an axiom is consistent, we discover that type inequality in the logic as it stands can hold by only refuting the existence of type isomorphism. As a result, your partial solution is, at least in principle, where it’s at. Enumerability is rather key to showing non-isomorphism. I’m not sure what the status of
nat = (nat -> nat)might be, but it is clear from outside the system that every inhabitant ofnat -> nathas a normal form, and that there are countably many normal forms: it’s at least plausible that there are consistent axioms or reflection principles which make the logic more intensional and which validate that hypothesis.Automating Cardinality Arguments
I can see two steps you might take to improve on the present situation. The less radical step is to improve your generic technology for making these cardinality arguments by better use of reflection. You’re ideally placed to do so, because in general, you’re looking to show that a finite set is distinct from some larger set. Suppose we have some notion of
DList A, a list of distinct elements ofA. If you can construct an exhaustiveDList Aand a longerDList B, then you can disproveA = B.There’s a lovely definition of DList by induction-recursion, but Coq doesn’t have induction-recursion. Fortunately, it’s one of those definitions we can simulate by careful use of indexing. Forgive my informal syntax, but let’s have
That’s
dfor “distinct”. If a set already has decidable equality, you can equip it withdvery easily. A large set can be equipped with an adequatedfor our purposes with not much work. And actually, that’s the crucial step: following the wisdom of the SSReflect team, we take advantage of the smallness of our domain by working withboolrather thanProp, and make the computer do the heavy lifting.Now, let us have
where the index is the freshness test for the list
And if you like, you can define
DListwrappingDListBodyexistentially. Perhaps that’s actually hiding information we want, though, because to show such a thing exhaustive goes like this:So if you can write down a DListBody for a finite enumeration, you can prove it exhaustive just by a case analysis with trivial subgoals.
You then need only make the pigeonholing argument once. When you want to disprove an equality between types (assuming you already have suitable candidates for
d), you exhaustively enumerate the smaller and exhibit a longer list from the larger, and that’s it.Working in a Universe
The more radical alternative is to question why you are getting these goals in the first place, and whether they really mean what you want them to. What are types supposed to be, really? There are multiple possible answers to that question, but it is at least open that they are in some sense “cardinalities”. If you want to think of types as being more concrete and syntactic, distinct if they are built by distinct constructions, then you may need to equip types with a more concrete representation by working in a universe. You define an inductive datatype of “names” for types, together with the means to decode names as types, then you reframe your development in terms of names. You should find that inequality of names follows by ordinary constructor discrimination.
The snag is that universe constructions can be a bit tricky in Coq, again because induction-recursion is unsupported. It depends heavily on what types you need to consider. Maybe you can define inductively some
U : Setthen implement a recursive decoderT : U -> Set. That’s certainly plausible for universes of simple types. If you want a universe of dependent types, things get a bit sweatier. You can at least do this muchbut note that the domain of
PIis unencoded inSet, not inU. The inductive-recursive Agdans can get over this, definingUandTsimultaneouslybut Coq won’t have that. Again, the workaround is to use indexing. Here the cost is that
Uis inevitably large.But you can still get a lot of stuff done with a universe built that way. For example, one can equip such a universe with a computationally effective extensional equality.