# How or why does intutionistic logic proof negations from within the theory, constructively?

I'm having a little of a cognitive dissonance why, in intuitionistic logic, it's possible to show stentences like

$(\neg A \land \neg B) \implies \neg(A\lor B).$

In plain text: If 'A isn't true' as well as 'B isn't true', then neither is 'A or B'.

In terms of types, using the notation

$A\implies B\equiv A\to B,\hspace{.5cm} \neg A\equiv A\to 0,\hspace{.5cm} A\land B\equiv A\times B,\hspace{.5cm} A\lor B\equiv A+ B,$

where $0$ is the empty type (it comes with no type constructors and is said to have no terms), that statement reads

$((A\to 0)\times (B\to 0))\to (A+B)\to 0.$

To demonstrate what I do understand about it, here how I understand the constructive proof of the above statement:

Firstly, the "$(A\to 0)\times (B\to 0)$"-part: If we have $(A\to 0)$ and $(B\to 0)$, this means we have two functions $f:(A\to 0)$ and $g:(B\to 0)$ at hand, which demonstrate absurdness. E.g. if we would have $a:A$, then $f(a):0$, so $f(a)$ is a term of the empty type. More formally, we have a pair $p:=(f,g)$ of type $(A\to 0)\times (B\to 0)$, where we can project out the two absurd functions $\pi_1(p)=f$ and $\pi_2(p)=g$.

Regarding the "$(A+B)$"-part, one possible definition of the type construction of "or" is given by case analysis. With $p$ and hence $f$ and $g$, we have one functions for each case $A$ and $B$, and so a function $h$ of type $(A+B)\to 0$ can readily be constructed. Say we use $a:A$ and $b:B$ as the generic terms, then we can set $h(a):=f(a)$ and $h(b):=g(b)$.

In total, we can give a proof of $((A\to 0)\times (B\to 0))\to (A+B)\to 0$ is a function $P$ which takes a $p$ and a $c$ (either $a$ or $b$) and computes a term of $0$.

$P:=\lambda p.\ \lambda c.\ \mathrm{if}\ (c:A)\ \mathrm{do}\ \pi_1(p)(c)\ \mathrm{else}\ \pi_2(p)(c)$.

Now a more simple example is the proof of $A\implies \neg\neg A$, or

$A\to ((A\to 0)\to 0).$

If we have $A$, and hence a term $a:A$, then if we have a function $f:A\to 0$, we have that $f(a):0$ and so a function proving the above is

$P:=\lambda a.\ \lambda f.\ f(a)$

and of type $A\to ((A\to 0)\to 0).$

Conversely, we can't without further information proof $\neg\neg A\implies A$, or $((A\to 0)\to 0)\to A,$ because from a given function $f:((A\to 0)\to 0)$ we can't reach into its domain and say something about $A$.

Okay, now this this is out of the way, here is my problem:

The proofs of a negation consists of showing a way of how having a term of the assumption type would lead to the result of $0$ being inhabited. And indeed, whenever we successfully proof a negation $\dots \to 0$, we did this by constructing a function which when evaluated would return a term of of type $0$. Now the type $0$ deliberately comes with no type construction rules and so to make the above constructions, we needed to use a function which was also of type $\dots \to 0$. Consequently and more concretely, in both cases above the eventual proofs are functions including a lambda abstraction over such an "absurd function". Now if hence every absurd function is a function of other absurd functions, we can at best concatenate these functions but we can never evaluate any such function. Indeed, if our theory is consistent this already means we can never end up with $f(\dots):0$. But if we can only construct function which can't be evaluated, how on earth is this a "constructive proof"? This isn't a function, it's something to concatenate. Or put differently, we didn't construct a proof, we constructed a line of thought. At best, we can flee to the meta-theory and judge "if we end up with this thing with the right type, then I accept it as proof". But is then completely removed from the semantic content of what was to proof and I also don't know if the meta-logic can be intuitionistic.

@CarlMummert: As I try to explain above, and what I feel two answer now don't adress, is that my problem does not lie in "I proof the absurd by using something absurd", i.e. I'm okay with $\neg A\equiv A\implies ⊥$, but in the representation of this in the Curry-Howard isomorphism and the lambda calculus. I thought about it more now, and my issue likely is in the fact that one defines $\neg A\equiv A\to 0$, although I see no good argument to express the negation using the already given function concept. It turns out to work, so that's one possible justification, but it's a little step that undermines the meaning of the function concept. The problem here is that, as I describe above, for a proof of a negation we describe or "construct" something, which isn't a function. It has the form $\lambda f.\ \mathrm{foo}$, but you can't even pass the totality of its arguments to it, as these arguments not constructable (which happens to be exactly what we want to demonstrate). The proof-object we hence constructed can only be used to have its type checked. The purpose of involving $0$, the type without constructors, in the definition of $\neg A$ here is seemingly only to guaranty we can't proof $A\to 0$ in any other accidental way. Can't we not make $\neg A$ a standalone type which represents the notion of proof of the absurd? Well anyway, if there is no reference other an appealing alternative, I'll just go with the top answer, because I the engineering answer "it just works" is an answer too, I guess.

-
Note: the name "intuitionistic" is not really descriptive. On the contrary, I would say. – Daniel Fischer Dec 14 '13 at 15:12
That's just your opinion, moulded by years of studying classical mathematics. Ask someone not exposed to mathematics what a proof of "A or B" should be, and you will surely be told it is either a proof of A or a proof of B – exactly as in intuitionistic logic. – Zhen Lin Dec 14 '13 at 15:29
The proof of a universal statement such as $(\forall x)[P(x)\to Q(x)]$, when we do not have a proof of $(\exists x)P(x)$, seems to be just as much a "line of thought" rather than an actual construction. So I am not sure that negation is directly relevant. The real issue here is the BHK interpretation of the implication operator. – Carl Mummert Dec 14 '13 at 15:47
@NickKidman To say that a function that cannot be called is not a function is absurd. That's like saying that dead code in a program is not really code. – Zhen Lin Dec 15 '13 at 1:01
@ZhenLin: Absurd? I feel it's like a "painting" which can't be looked at. But if you say so. It's probably a semantical question. – NikolajK Dec 15 '13 at 3:31

Another way to understand $\bot$ that you might find more intuitive is that it is the type of a program that doesn't return a value, perhaps because it threw a fatal exception that terminated the computation, or because it went into an infinite loop and never completed. In fact if you construct such a function in a language like Haskell where such things are possible, the type inferencer will infer a type that is compatible with $\bot$. For example

loop x = loop x

says that to calculate $loop(x)$ for some argument $x$, you first compute $loop(x)$, and that is the answer; this is exactly an infinite loop. Haskell obligingly infers the type to be $a\to b$, where $a$ and $b$ are completely unconstrained, so in particular $a\to\bot = \lnot a$ is a valid instance of this type.

Under this interpretation, we understand $$(\lnot A \land\lnot B) \to \lnot(A\lor B)$$ to mean the following: We are given a function which takes an argument of type $A$ and goes into an infinite loop (that's $\lnot A$), and function which takes an argument of type $B$ and goes into an infinite loop (that's $\lnot B$), and we want to construct from this a function which takes either an $A$ or a $B$ and goes into an infinite loop (that's $\lnot(A\lor B)$).

If we were working in Haskell or some similar language this would be completely trivial, as the $\lnot a$ types are all inhabited axiomatically (the definition above being an example) but in the world of logic there are fewer axioms: we are effectively working in a programming language where we can't start an infinite loop unless someone actually gives us a function that does so when it is called. So the conclusion is not foregone and we actually need the premisses.

As you know, constructing the function whose type is actually $\lnot A \land\lnot B \to \lnot(A\lor B)$ is straightforward: It gets its argument, performs case analysis on it to decide whether it is an $A$ or a $B$, and then calls one of the other components of the input, which initiates the required infinite loop.

You can read through all that again, replacing “goes into an infinite loop” with “throws a fatal exception” and it should still make sense: Your function gets two functions, one of which throws an exception if given an argument of type $A$, and the other of which throws an exception if given an argument of type $B$. It is also given either an $A$ or a $B$, but it doesn't know ahead of time which. It has no way itself to throw an exception. How can it still guarantee that one is thrown?

So to answer your question about $\bot$ representing an uninhabited type, no, we don't only do it out of engineering necessity. We do it because it's one of the ways of understanding types that involve $\bot$. But it is only one of the possible ways of understanding those types. A program that never yields any value, because it dies, or because it never terminates, is also a perfectly good (non-)producer of $\bot$ values, and these interpretations of $\bot$ values are equally important—much more so actually, because as you observed you can't actually exhibit a producer of $\bot$ values in practice, but programs that loop or that abort come up in practice all the time.

The logic doesn't distinguish between computations that abort, computations that get stuck, and computations that are impossible because they return an impossible value, whereas all those things are quite different operationally. Although they're logically equivalent it is sometimes easier to think of one of them or another when trying to understand the operational meaning of a proposition. Instead of thinking about programs that would produce impossible values but don't because they can't, which as you observed is very strange, you can think about programs that die. It comes to the same thing.

-
Thanks! Is the "$⊥$" in the first line rather supposed to be a negation of a type? Otherwise, if $⊥$ itself is the type of programs which turns forever, then it shouldn't be empty. Regarding the last paragrgraphs, I want to emphasise that I make the case that the proof of $((A→0)×(B→0))→(A+B)→0$, considered as function, doesn't just never output a value like a loop, it can't even be given the totality of its arguments. Related to this, you also say in Haskell $\neg A$ is inhabited for all $A$. So can general Haskell have a reasonable proof interpretation if all negations are already inhabited? – NikolajK Dec 15 '13 at 19:27
@Nick: $\top$ is "tautology" or "true" and $\bot$ is "contradiction" or "false". – Hurkyl Dec 15 '13 at 19:34
@Hurkyl: Yo, that I know, I used $⊥$ in the question above. It's just surprising he wants it to be a function type in the first line. Not specifying a specific domain, it would be all non-terminating programs over Type. Some lines later he seems to say loop, with domain $a$, should have type $a\to ⊥$. – NikolajK Dec 15 '13 at 19:40
$\bot$ is exactly the empty type; there are no values of type $\bot$. A program that takes an argument of type $A$ and then runs forever is $A\to\bot$. – MJD Dec 15 '13 at 22:36
@MJD: Yeah, is the consistent with "Another way to understand $⊥$ that you might find more intuitive is that it is the type of a program that doesn't return a value, perhaps because it threw a fatal exception that terminated the computation, or because it went into an infinite loop and never completed." – NikolajK Dec 16 '13 at 9:45

The constructive version of proving $\lnot A$ is (as you noted) giving a constructive procedure that derives from any hypothetical instance of $A$ an absurdity; this demonstrates that having an instance of $A$ is absurd. So to prove (from hypotheses) $\lnot(A \lor B)$ one gives such a constructive proof assuming an instance of $A\lor B$, and you did that.

Now you complain that this is only a hypothetical construction, based on an impossible assumption. But given that the goal is to prove that the assumption is impossible, what more constructive alternative does one have? There isn't any. The conclusion that any negative statement in a constructive theory is somewhat weak (compared to positive statements) is quite justified though. In particular $\lnot\lnot A$ is not sufficient to deduce $A$, in general (it is though for many particular instances of $A$, for instance it is always so if $A$ is itself negative: $A=\lnot B$, since one does have $\lnot\lnot\lnot B\to\lnot B$).

-
Exactly: "the goal is to prove that the assumption is impossible". How else could that be done, constructively, than by providing a constructive demonstration that the assumption would lead to something that is already known to be impossible? – Carl Mummert Dec 14 '13 at 15:31

I'd say this proof is more easily understood if you generalize it by replacing $\bot$ with an arbitrary proposition $C$:

$$\left((A\Rightarrow C)\land (B\Rightarrow C)\right)\Rightarrow \left((A\lor B)\Rightarrow C\right)$$

But this is just the elimination rule for $\lor$ in disguise in natural deduction:

$$\underline{\Gamma\vdash A\lor B \quad \Gamma,A\vdash C \quad \Gamma,B\vdash C}\\ \Gamma\vdash C$$

The proof can be also easily demonstrated in Haskell:

proof :: ((a -> c), (b -> c)) -> (Either a b -> c)
proof (f, _) (Left  x) = f x
proof (_, g) (Right y) = g y

or just (relating to the elimination of Either):

proof :: ((a -> c), (b -> c)) -> (Either a b -> c)
proof = uncurry either

Update: I good way how to interpret $A\to\bot$ is as "terminate current evaluation and start somewhere else while transferring $A$ there".

This isn't very meaningful in pure intuitionistic settings, but becomes much more interesting when we introduce the call-with-current-continuation operator (AKA call/cc).

A language with call/cc allows to capture a particular state of the evaluation process of a term and resume the state, even repeatedly. One of the ways how to embed call/cc into the type system is to create a primitive operator of type corresponding to the double negation elimination:

$$\mbox{call/cc} : ((A\to\bot)\to\bot)\to A$$

(and now the type system now corresponds to the classical logic). A function given as an argument to call/cc is of type $(A\to\bot)\to\bot$. This means it can never compute a result, the only thing it can do is escape the evaluation by taking a value of type $A$ and calling an "escape function" of type $A\to\bot$ on it.

What gives call/cc its strength is that one can use an "escape function" anywhere, in particular outside of the scope of a call/cc function, to "reset" the evaluation to a particular point.

As an example, consider the proof of the law of excluded middle in intuitionistic logic plus the double negation elimination. Let me use the Haskell language to demonstrate it, assuming we have a hypothetical call/cc implementation:

import Data.Void

type Neg a = a -> Void

callcc :: Neg (Neg a) -> a
callcc = undefined

doubleNeg :: Either a (Neg a)
doubleNeg = callcc $\esc -> esc (Right (\x -> esc (Left x))) Of course doubleNeg has no way of knowing if a is inhabited or not, so it sort of "cheats". It returns Right, as if claiming that a is uninhabited, with a special escape function crafted for Neg a. If someone ever constructs a value x of a and applies this function to it, the whole computation beyond the point of the call to doubleNeg is thrown away and the evaluation resumes from that saved point, returning Left x. - That's nice, but I don't think you address the question here at all. I do understand the function/program/proof of the statement and that's why I even wrote the lengthy proof into the question itself, though then the question is about how any negation is proven in the first place. – NikolajK Jul 7 '14 at 20:28 @NikolajK True. I extended the answer showing how one can interpret$A\to\bot$. – Petr Pudlák Jul 11 '14 at 21:10 I'm not quite sure what you're asking, but it looks to me like "where do absurdities originate?". When you have a sum type defined, like a Peano$Int$($Z$,$S Z$,$S (S Z)$,$\dots)$, it's an absurdity for a variable$x: Int$to match anything other than exactly one of the available forms$Z$or$S q$where$q : Int$. Defining a sum type implicitly gives you primitives like "$x=Z \rightarrow \neg (\exists q : x = S q)$" and "$x = Z \lor \exists q : x = S q\$" that you need to assert the properties of the type, and you can use them to construct absurdities.

-