OCaml Adding Natural Numbers - function

I'm learning OCaml in school and recently came came across a program for an assignment that I couldn't understand, and was hoping if somebody could explain it to me. Here's the code:
(* Natural numbers can be defined as follows:
type = ZERO | SUCC of nat;;
For instance, two = SUCC (SUCC ZERO) and three = SUCC (SUCC (SUCC ZERO)).
Write a function 'natadd' that adds two natural numbers in this fashion. *)
# type = ZERO | SUCC of nat;;
# let two = SUCC (SUCC ZERO);;
# let three = SUCC (SUCC (SUCC ZERO));;
# let rec natadd : nat -> nat -> nat =
fun n1 n2 ->
match n1 with
| ZERO -> n2
| SUCC n1 -> SUCC (natadd n1 n2)
;;
Here's a sample output for the code:
# natadd two three;;
- : nat = SUCC (SUCC (SUCC (SUCC (SUCC ZERO))))
What I don't understand is the match-with statement. Does it mean that if n1 is non-zero, then it adds SUCC and uses [SUCC n1] as a new argument in place of n1 for the recursive call?

No, it doesn't use SUCC n1 as the argument of the recursive call, it uses only n1 (the part of the matched SUCC) as the argument. The SUCC is then applied to the result of the recursive call.
The code might be a bit confusing because there are two variables with the name n1. Better might be
let rec natadd : nat -> nat -> nat = fun a b ->
match a with
| ZERO -> b
| SUCC a_pred -> SUCC (natadd a_pred b)

Related

Finding inverse functions [duplicate]

In pure functional languages like Haskell, is there an algorithm to get the inverse of a function, (edit) when it is bijective? And is there a specific way to program your function so it is?
In some cases, yes! There's a beautiful paper called Bidirectionalization for Free! which discusses a few cases -- when your function is sufficiently polymorphic -- where it is possible, completely automatically to derive an inverse function. (It also discusses what makes the problem hard when the functions are not polymorphic.)
What you get out in the case your function is invertible is the inverse (with a spurious input); in other cases, you get a function which tries to "merge" an old input value and a new output value.
No, it's not possible in general.
Proof: consider bijective functions of type
type F = [Bit] -> [Bit]
with
data Bit = B0 | B1
Assume we have an inverter inv :: F -> F such that inv f . f ≡ id. Say we have tested it for the function f = id, by confirming that
inv f (repeat B0) -> (B0 : ls)
Since this first B0 in the output must have come after some finite time, we have an upper bound n on both the depth to which inv had actually evaluated our test input to obtain this result, as well as the number of times it can have called f. Define now a family of functions
g j (B1 : B0 : ... (n+j times) ... B0 : ls)
= B0 : ... (n+j times) ... B0 : B1 : ls
g j (B0 : ... (n+j times) ... B0 : B1 : ls)
= B1 : B0 : ... (n+j times) ... B0 : ls
g j l = l
Clearly, for all 0<j≤n, g j is a bijection, in fact self-inverse. So we should be able to confirm
inv (g j) (replicate (n+j) B0 ++ B1 : repeat B0) -> (B1 : ls)
but to fulfill this, inv (g j) would have needed to either
evaluate g j (B1 : repeat B0) to a depth of n+j > n
evaluate head $ g j l for at least n different lists matching replicate (n+j) B0 ++ B1 : ls
Up to that point, at least one of the g j is indistinguishable from f, and since inv f hadn't done either of these evaluations, inv could not possibly have told it apart – short of doing some runtime-measurements on its own, which is only possible in the IO Monad.
                                                                                                                                   ⬜
You can look it up on wikipedia, it's called Reversible Computing.
In general you can't do it though and none of the functional languages have that option. For example:
f :: a -> Int
f _ = 1
This function does not have an inverse.
Not in most functional languages, but in logic programming or relational programming, most functions you define are in fact not functions but "relations", and these can be used in both directions. See for example prolog or kanren.
Tasks like this are almost always undecidable. You can have a solution for some specific functions, but not in general.
Here, you cannot even recognize which functions have an inverse. Quoting Barendregt, H. P. The Lambda Calculus: Its Syntax and Semantics. North Holland, Amsterdam (1984):
A set of lambda-terms is nontrivial if it is neither the empty nor the full set. If A and B are two nontrivial, disjoint sets of lambda-terms closed under (beta) equality, then A and B are recursively inseparable.
Let's take A to be the set of lambda terms that represent invertible functions and B the rest. Both are non-empty and closed under beta equality. So it's not possible to decide whether a function is invertible or not.
(This applies to the untyped lambda calculus. TBH I don't know if the argument can be directly adapted to a typed lambda calculus when we know the type of a function that we want to invert. But I'm pretty sure it will be similar.)
If you can enumerate the domain of the function and can compare elements of the range for equality, you can - in a rather straightforward way. By enumerate I mean having a list of all the elements available. I'll stick to Haskell, since I don't know Ocaml (or even how to capitalise it properly ;-)
What you want to do is run through the elements of the domain and see if they're equal to the element of the range you're trying to invert, and take the first one that works:
inv :: Eq b => [a] -> (a -> b) -> (b -> a)
inv domain f b = head [ a | a <- domain, f a == b ]
Since you've stated that f is a bijection, there's bound to be one and only one such element. The trick, of course, is to ensure that your enumeration of the domain actually reaches all the elements in a finite time. If you're trying to invert a bijection from Integer to Integer, using [0,1 ..] ++ [-1,-2 ..] won't work as you'll never get to the negative numbers. Concretely, inv ([0,1 ..] ++ [-1,-2 ..]) (+1) (-3) will never yield a value.
However, 0 : concatMap (\x -> [x,-x]) [1..] will work, as this runs through the integers in the following order [0,1,-1,2,-2,3,-3, and so on]. Indeed inv (0 : concatMap (\x -> [x,-x]) [1..]) (+1) (-3) promptly returns -4!
The Control.Monad.Omega package can help you run through lists of tuples etcetera in a good way; I'm sure there's more packages like that - but I don't know them.
Of course, this approach is rather low-brow and brute-force, not to mention ugly and inefficient! So I'll end with a few remarks on the last part of your question, on how to 'write' bijections. The type system of Haskell isn't up to proving that a function is a bijection - you really want something like Agda for that - but it is willing to trust you.
(Warning: untested code follows)
So can you define a datatype of Bijection s between types a and b:
data Bi a b = Bi {
apply :: a -> b,
invert :: b -> a
}
along with as many constants (where you can say 'I know they're bijections!') as you like, such as:
notBi :: Bi Bool Bool
notBi = Bi not not
add1Bi :: Bi Integer Integer
add1Bi = Bi (+1) (subtract 1)
and a couple of smart combinators, such as:
idBi :: Bi a a
idBi = Bi id id
invertBi :: Bi a b -> Bi b a
invertBi (Bi a i) = (Bi i a)
composeBi :: Bi a b -> Bi b c -> Bi a c
composeBi (Bi a1 i1) (Bi a2 i2) = Bi (a2 . a1) (i1 . i2)
mapBi :: Bi a b -> Bi [a] [b]
mapBi (Bi a i) = Bi (map a) (map i)
bruteForceBi :: Eq b => [a] -> (a -> b) -> Bi a b
bruteForceBi domain f = Bi f (inv domain f)
I think you could then do invert (mapBi add1Bi) [1,5,6] and get [0,4,5]. If you pick your combinators in a smart way, I think the number of times you'll have to write a Bi constant by hand could be quite limited.
After all, if you know a function is a bijection, you'll hopefully have a proof-sketch of that fact in your head, which the Curry-Howard isomorphism should be able to turn into a program :-)
I've recently been dealing with issues like this, and no, I'd say that (a) it's not difficult in many case, but (b) it's not efficient at all.
Basically, suppose you have f :: a -> b, and that f is indeed a bjiection. You can compute the inverse f' :: b -> a in a really dumb way:
import Data.List
-- | Class for types whose values are recursively enumerable.
class Enumerable a where
-- | Produce the list of all values of type #a#.
enumerate :: [a]
-- | Note, this is only guaranteed to terminate if #f# is a bijection!
invert :: (Enumerable a, Eq b) => (a -> b) -> b -> Maybe a
invert f b = find (\a -> f a == b) enumerate
If f is a bijection and enumerate truly produces all values of a, then you will eventually hit an a such that f a == b.
Types that have a Bounded and an Enum instance can be trivially made RecursivelyEnumerable. Pairs of Enumerable types can also be made Enumerable:
instance (Enumerable a, Enumerable b) => Enumerable (a, b) where
enumerate = crossWith (,) enumerate enumerate
crossWith :: (a -> b -> c) -> [a] -> [b] -> [c]
crossWith f _ [] = []
crossWith f [] _ = []
crossWith f (x0:xs) (y0:ys) =
f x0 y0 : interleave (map (f x0) ys)
(interleave (map (flip f y0) xs)
(crossWith f xs ys))
interleave :: [a] -> [a] -> [a]
interleave xs [] = xs
interleave [] ys = []
interleave (x:xs) ys = x : interleave ys xs
Same goes for disjunctions of Enumerable types:
instance (Enumerable a, Enumerable b) => Enumerable (Either a b) where
enumerate = enumerateEither enumerate enumerate
enumerateEither :: [a] -> [b] -> [Either a b]
enumerateEither [] ys = map Right ys
enumerateEither xs [] = map Left xs
enumerateEither (x:xs) (y:ys) = Left x : Right y : enumerateEither xs ys
The fact that we can do this both for (,) and Either probably means that we can do it for any algebraic data type.
Not every function has an inverse. If you limit the discussion to one-to-one functions, the ability to invert an arbitrary function grants the ability to crack any cryptosystem. We kind of have to hope this isn't feasible, even in theory!
In some cases, it is possible to find the inverse of a bijective function by converting it into a symbolic representation. Based on this example, I wrote this Haskell program to find inverses of some simple polynomial functions:
bijective_function x = x*2+1
main = do
print $ bijective_function 3
print $ inverse_function bijective_function (bijective_function 3)
data Expr = X | Const Double |
Plus Expr Expr | Subtract Expr Expr | Mult Expr Expr | Div Expr Expr |
Negate Expr | Inverse Expr |
Exp Expr | Log Expr | Sin Expr | Atanh Expr | Sinh Expr | Acosh Expr | Cosh Expr | Tan Expr | Cos Expr |Asinh Expr|Atan Expr|Acos Expr|Asin Expr|Abs Expr|Signum Expr|Integer
deriving (Show, Eq)
instance Num Expr where
(+) = Plus
(-) = Subtract
(*) = Mult
abs = Abs
signum = Signum
negate = Negate
fromInteger a = Const $ fromIntegral a
instance Fractional Expr where
recip = Inverse
fromRational a = Const $ realToFrac a
(/) = Div
instance Floating Expr where
pi = Const pi
exp = Exp
log = Log
sin = Sin
atanh = Atanh
sinh = Sinh
cosh = Cosh
acosh = Acosh
cos = Cos
tan = Tan
asin = Asin
acos = Acos
atan = Atan
asinh = Asinh
fromFunction f = f X
toFunction :: Expr -> (Double -> Double)
toFunction X = \x -> x
toFunction (Negate a) = \a -> (negate a)
toFunction (Const a) = const a
toFunction (Plus a b) = \x -> (toFunction a x) + (toFunction b x)
toFunction (Subtract a b) = \x -> (toFunction a x) - (toFunction b x)
toFunction (Mult a b) = \x -> (toFunction a x) * (toFunction b x)
toFunction (Div a b) = \x -> (toFunction a x) / (toFunction b x)
with_function func x = toFunction $ func $ fromFunction x
simplify X = X
simplify (Div (Const a) (Const b)) = Const (a/b)
simplify (Mult (Const a) (Const b)) | a == 0 || b == 0 = 0 | otherwise = Const (a*b)
simplify (Negate (Negate a)) = simplify a
simplify (Subtract a b) = simplify ( Plus (simplify a) (Negate (simplify b)) )
simplify (Div a b) | a == b = Const 1.0 | otherwise = simplify (Div (simplify a) (simplify b))
simplify (Mult a b) = simplify (Mult (simplify a) (simplify b))
simplify (Const a) = Const a
simplify (Plus (Const a) (Const b)) = Const (a+b)
simplify (Plus a (Const b)) = simplify (Plus (Const b) (simplify a))
simplify (Plus (Mult (Const a) X) (Mult (Const b) X)) = (simplify (Mult (Const (a+b)) X))
simplify (Plus (Const a) b) = simplify (Plus (simplify b) (Const a))
simplify (Plus X a) = simplify (Plus (Mult 1 X) (simplify a))
simplify (Plus a X) = simplify (Plus (Mult 1 X) (simplify a))
simplify (Plus a b) = (simplify (Plus (simplify a) (simplify b)))
simplify a = a
inverse X = X
inverse (Const a) = simplify (Const a)
inverse (Mult (Const a) (Const b)) = Const (a * b)
inverse (Mult (Const a) X) = (Div X (Const a))
inverse (Plus X (Const a)) = (Subtract X (Const a))
inverse (Negate x) = Negate (inverse x)
inverse a = inverse (simplify a)
inverse_function x = with_function inverse x
This example only works with arithmetic expressions, but it could probably be generalized to work with lists as well. There are also several implementations of computer algebra systems in Haskell that may be used to find the inverse of a bijective function.
No, not all functions even have inverses. For instance, what would the inverse of this function be?
f x = 1

Implement primitive recursive factorial in haskell

I am currently trying to implement primitive recursive factorial in Haskell.
I'm using the function recNat, as a recursor. That is:
recNat :: a -> (Nat -> a -> a) -> Nat -> a
recNat a _ Zero = a
recNat a h (Succ n) = h n (recNat a h n)
This is our attempt, but can't quite figure out what's wrong
factR :: Nat -> Nat
factR Zero = Succ Zero
factR (Succ m) = recNat (Succ m) (\ _ y -> y) (factR m)
I was also trying to implement the exponential function, but it seems even more confusing.
In order to implement a factorial, we can implement a function for multiplication. For the multiplication function, we need the addition function
data Nat = Zero | Succ Nat
add :: Nat -> Nat -> Nat
add a Zero = a
add a (Succ b) = Succ (add a b)
mul :: Nat -> Nat -> Nat
mul a Zero = Zero
mul a (Succ b) = add a (mul a b)
Then the factorial function just comes down to:
fac :: Nat -> Nat
fac Zero = Succ Zero
fac (Succ a) = mul (Succ a) (fac a)

Primitive Recursive Functions Excercise in Haskell

For a functional programming excercise I am required to apply primitive recursive functions in haskell. However I do not quite understand yet the definition (and application) of this type of functions.
We are presented the data type Nat to be used, its constructor is:
data Nat = Zero | Succ Nat
To my understanding this means that the type "Nat" can be either a Zero or a Natural Succesor.
Then we have a recursor:
recNat :: a -> (Nat -> a -> a) -> Nat -> a
recNat a _ Zero = a
recNat a h (Succ n) = h n (recNat a h n)
Which I understand is meant to apply recursion to a function?
And I've also been given an example of an addition function using the recursor:
addR :: Nat -> Nat -> Nat
addR m n = recNat n (\ _ y -> Succ y) m
But I don't get how it works, it uses the recNat function with the given two Nats, and also uses an anonymous function as input for recNat ( that is the part I'm not sure what it does! )
So my main issue is what does this do in the function exactly > \ _ y -> Succ y
I'm suppossed to apply this same recursor (RecNat) to apply other operations to Nat, but I'm stuck still trying to understand the example!
You’re right that data Nat = Zero | Succ Nat means that a Nat may be Zero or the Successor of another Nat; this represents natural numbers as a linked list, i.e.:
zero, one, two, three, four, five :: Nat
zero = Zero
one = Succ Zero -- or: Succ zero
two = Succ (Succ Zero) -- Succ one
three = Succ (Succ (Succ Zero)) -- Succ two
four = Succ (Succ (Succ (Succ Zero))) -- Succ three
five = Succ (Succ (Succ (Succ (Succ Zero)))) -- Succ four
-- …
The function of recNat is to fold over a Nat: recNat z k takes a Nat and “counts down” by ones to the final Zero, calling k on every intermediate Succ, and replacing the Zero with z:
recNat z k three
recNat z k (Succ (Succ (Succ Zero)))
-- by second equation of ‘recNat’:
k two (recNat z k two)
k (Succ (Succ Zero)) (recNat z k (Succ (Succ Zero)))
-- by second equation of ‘recNat’:
k two (k one (recNat z k one))
k (Succ (Succ Zero)) (k (Succ Zero) (recNat z k (Succ Zero)))
-- by second equation of ‘recNat’:
k two (k one (k zero (recNat z k zero)))
k (Succ (Succ Zero)) (k (Succ Zero) (k Zero (recNat z k Zero)))
-- by first equation of ‘recNat’:
k two (k one (k zero z))
k (Succ (Succ Zero)) (k (Succ Zero) (k Zero z))
The lambda \ _ y -> Succ y has type a -> Nat -> Nat; it just ignores its first argument and returns the successor of its second argument. Here’s an illustration of how addR works to compute the sum of two Nats:
addR two three
addR (Succ (Succ Zero)) (Succ (Succ (Succ Zero)))
-- by definition of ‘addR’:
recNat three (\ _ y -> Succ y) two
recNat (Succ (Succ (Succ Zero))) (\ _ y -> Succ y) (Succ (Succ Zero))
-- by second equation of ‘recNat’:
(\ _ y -> Succ y) one (recNat three (\ _ y -> Succ y) one)
(\ _ y -> Succ y) (Succ Zero) (recNat (Succ (Succ (Succ Zero))) (\ _ y -> Succ y) (Succ Zero))
-- by application of the lambda:
Succ (recNat three (\ _ y -> Succ y) one)
Succ (recNat (Succ (Succ (Succ Zero))) (\ _ y -> Succ y) (Succ Zero))
-- by second equation of ‘recNat’:
Succ ((\ _ y -> Succ y) zero (recNat three (\ _ y -> Succ y) zero))
Succ ((\ _ y -> Succ y) zero (recNat (Succ (Succ (Succ Zero))) (\ _ y -> Succ y) zero))
-- by application of the lambda:
Succ (Succ (recNat three (\ _ y -> Succ y) zero))
Succ (Succ (recNat (Succ (Succ (Succ Zero))) (\ _ y -> Succ y) zero))
-- by first equation of ‘recNat’:
Succ (Succ three)
Succ (Succ (Succ (Succ (Succ Zero))))
-- by definition of ‘five’:
five
Succ (Succ (Succ (Succ (Succ Zero))))
As you can see, what’s happening here is we’re essentially taking off each Succ from one number and putting it on the end of the other, or equivalently, replacing the Zero in one number with the other number, i.e., the steps go like this:
1+1+0 + 1+1+1+0 2 + 3
1+(1+0 + 1+1+1+0) 1+(1 + 3)
1+1+(0 + 1+1+1+0) 1+1+(0 + 3)
1+1+(1+1+1+0) 1+1+(3)
1+1+1+1+1+0 5
The inner lambda always ignores its first argument with _, so it may be simpler to see how this works with a simpler definition of recNat that literally replaces Zero with a value z and Succ with a function s:
recNat' :: a -> (a -> a) -> Nat -> a
recNat' z _ Zero = z
recNat' z s (Succ n) = s (recNat z s n)
Then addition is slightly simplified:
addR' m n = recNat' n Succ m
This literally says “to compute the sum of m and n, add one m times to n”.
You may find it easier to play around with these numbers if you make a Num instance and Show instance for them:
{-# LANGUAGE InstanceSigs #-} -- for explicitness
instance Num Nat where
fromInteger :: Integer -> Nat
fromInteger n
| n <= 0 = Zero
| otherwise = Succ (fromInteger (n - 1))
(+) :: Nat -> Nat -> Nat
(+) = addR
(*) :: Nat -> Nat -> Nat
(*) = … -- left as an exercise
(-) :: Nat -> Nat -> Nat
(-) = … -- left as an exercise
abs :: Nat -> Nat
abs n = n
signum :: Nat -> Nat
signum Zero = Zero
signum Succ{} = Succ Zero
negate :: Nat -> Nat
negate n = n -- somewhat hackish
instance Show Nat where
show n = show (recNat' (+ 1) 0 n :: Int)
Then you can write 2 + 3 :: Nat and have it display as 5.
Roughly, recNat x f n computes
f (n-1) (f (n-2) (f (n-3) (... (f 0 x))))
So, it applies f to x for n times, each time also passing a "counter" as the first argument of f.
In your case \_ y -> ... ignores the "counter" argument. Hence
addR m n = recNat n (\ _ y -> Succ y) m
can be read as "to compute m+n, apply m times the function Succ to n". This effectively computes ((n+1)+1)+1... where there are m ones in the sum.
You can try to compute the product of two naturals in a similar way. Use \_ y -> ... and express multiplication as repeated addition. You'll need to use the already-defined addR for that.
Additional hint: after multiplication, if you want to compute the predecessor n-1, then the "counter" argument will be very handy, so don't discard that and use \x y -> ... instead. After that, you can derive (truncated) subtraction as repeated predecessor.

SML: Recursive addition test case syntax

I have written SML code to recursively add two numbers of datatype nat. A function peanify to convert integer to nat. A function decimal to convert nat to integer.
Wanted help with syntax to test fun plus.
Say I want to test for 2 + 1
datatype nat = Zero | Succ of nat
fun peanify (x:int) : nat =
if x=0 then Zero
else Succ (peanify (x-1))
fun decimal (n:nat) : int =
case n of
Zero => 0
| (Succ n) => 1 + (decimal n)
fun plus (x : nat) : (nat -> nat) =
case x of
Zero => (fn y => y)
| Succ x' => (fn y => Succ (plus x' y))
Expected Result-:
val it = Succ (Succ (Succ Zero)) : nat
You can write:
val it = plus (peanify 2) (peanify 1)

How do we know all Coq constructors are injective and disjoint?

According to this course, all constructors (for inductive types) are injective and disjoint:
...Similar principles apply to all inductively defined types: all
constructors are injective, and the values built from distinct
constructors are never equal. For lists, the cons constructor is
injective and nil is different from every non-empty list. For
booleans, true and false are unequal.
(And the inversion tactic based on this assumption)
I am just wondering how do we know this assumption holds?
How do we know that, e.g., we cannot define natural numbers based on
1) a Successor and maybe a "Double" constructor like this:
Inductive num: Type :=
| O : num
| S : num -> num
| D : num -> num.
and
2) some plus function so that one number 2 can be reached via two different sequences/routes of constructors, S (S O) and D (S O)?
What's the mechanism in Coq that ensures the above will never happen?
P.S.
I am not suggesting the above num example is possible. I am just wondering what makes it impossible.
Thanks
When you define an inductive data type in Coq, you are essentially
defining a tree type. Each constructor gives a kind of node that is
allowed to occur in your tree, and its arguments determine the
children and elements that that node can have. Finally, functions
defined on inductive types (with the match clause) can check the
constructors that were used to produce a value of that type in
arbitrary ways. This makes Coq constructors very different from
constructors you see in an OO language, for instance. An object
constructor is implemented as a regular function that initializes a
value of a given type; Coq constructors, on the other hand, are
enumerating the possible values that the representation of our type
allows. To understand this difference better, we can compare the
different functions we can define on an object in a traditional OO
language, and on an element of an inductive type in Coq. Let's use
your num type as an example. Here's an object-oriented definition:
class Num {
int val;
private Num(int v) {
this.val = v;
}
/* These are the three "constructors", even though they
wouldn't correspond to what is called a "constructor" in
Java, for instance */
public static zero() {
return new Num(0);
}
public static succ(Num n) {
return new Num(n.val + 1);
}
public static doub(Num n) {
return new Num(2 * n.val);
}
}
And here's a definition in Coq:
Inductive num : Type :=
| zero : num
| succ : num -> num
| doub : num -> num.
In the OO example, when we write a function that takes a Num
argument, there's no way of knowing which "constructor" was used to
produce that value, because this information is not stored in the
val field. In particular Num.doub(Num.succ(Num.zero())) and
Num.succ(Num.succ(Num.zero())) would be equal values.
In the Coq example, on the other hand, things change, because we can
determine which constructor was used to form a num value, thanks to
the match statement. For instance, using Coq strings, we could write
a function like this:
Require Import Coq.Strings.String.
Open Scope string_scope.
Definition cons_name (n : num) : string :=
match n with
| zero => "zero"
| succ _ => "succ"
| doub _ => "doub"
end.
In particular, even though your intended meaning for the constructors
implies that succ (succ zero) and doub (succ zero) should be
"morally" equal, we can distinguish them by applying the cons_name
function to them:
Compute cons_name (doub (succ zero)). (* ==> "doub" *)
Compute cons_name (succ (succ zero)). (* ==> "succ" *)
As a matter of fact, we can use match to distinguish between succ
and doub in arbitrary ways:
match n with
| zero => false
| succ _ => false
| doub _ => true
end
Now, a = b in Coq means that there is no possible way we can
distinguish between a and b. The above examples show why doub
(succ zero) and succ (succ zero) cannot be equal, because we can
write functions that don't respect the meaning that we had in mind
when we wrote that type.
This explains why constructors are disjoint. That they are injective
is actually also a consequence of pattern-matching. For instance,
suppose that we wanted to prove the following statement:
forall n m, succ n = succ m -> n = m
We can begin the proof with
intros n m H.
Leading us to
n, m : num
H : succ n = succ m
===============================
n = m
Notice that this goal is by simplification equivalent to
n, m : num
H : succ n = succ m
===============================
match succ n with
| succ n' => n' = m
| _ => True
end
If we do rewrite H, we obtain
n, m : num
H : succ n = succ m
===============================
match succ m with
| succ n' => n' = m
| _ => True
end
which simplifies to
n, m : num
H : succ n = succ m
===============================
m = m
At this point, we can conclude with reflexivity. This technique is
quite general, and is actually at the core of what inversion does.
There is none: the constructors O, S and D are indeed disjoint and injective but the semantics for nums you have in your head is not, as a function, injective.
That is why num would usually be considered to be a bad representation of the natural numbers: working up-to equivalence is quite annoying.