Some logicians say that 3 ``is evidence of'' int or that 3 ``witnesses'' int.
A data structure's type is a compound proposition.
LOGIC PYTHON A B ^i : ---------------- (a,b): A ^ B, if a: A, b: B A ^ B A ^ B ^e1 : ----------------- e[0]: A, if e: A ^ B A A ^ B ^e2 : ----------------- e[1]: B, if e: A ^ B BExample script:
x = (2+2, True) # x has type int ^ bool y = x[1] # y has type bool z = (y, x[0]) # z has type bool ^ intThe data structures were built using logic rules.
You can see a data structure in every logic proof. For example, for
p, q ^ r |- (p ^ r)
1. p premise
2. q ^ r premise
3. r ^e2 2
4. p ^ r ^i 1,3
This proof builds a pair from a p-value and a q ^ r-value.
Say that p == int, q == bool, and r == float.
The above proof constructs this pair:
1. a : int presume a is an int value
2. b : bool ^ float presume b is a pair holding a bool and a float
3. b[1]: float ^e2 2 extract the right value from the pair
4. (a, b[1]): int ^ float ^i 1,3 build a new pair
Arrays and structs are like pairs, where the index values go beyond just 0 and 1..
def isEven(x) : return (x % 2 == 0)converts an int argument into a bool answer --- it has type int --> bool. When we call the function, we are using the -->e logic rule: 3: int, so then isEven(3): bool.
In Java,
the typing information must be included in the coding of the function:
public bool isEven(int x) { return (x % 2 == 0) }
Here is how the logic rules match to the programming constructions:
LOGIC PYTHON
A --> B A
-->e : ---------------- f(a): B, if f: A --> A, a: A
B
| A assumption def f(x) : # assuming x: A
| ... ...
-->i: | B return e # e must have type B
---------------
A --> B
This logic proof:
(p ^ q) -> r, p |- q -> r
1. (p ^ q) -> r premise
2. p premise
| 3. q assumption
| 4. p ^ q ^i 2,3
| 5. r ->e 1,4
6. q -> r ->i 3-5
uses two global values to construct a new function:
1. g: (p ^ q) -> r presume g is a global function already built
2. a: p presume a is some global value we can use
| 3. x: q assume x is a new parameter name
| 4. (a,x): p ^ q ^i 2,3
| 5. g(a,x) ->e 1 4
6. def f(x):return g(a,x) : q -> r ->i 3,5 function code all assembled
Here are the tricks shown in class:
def Eq(x1) : # has type int -> (int -> bool)
def Eqq(x2) :
return x1 == x2
return Eqq
f = Eq(4) # has type int -> bool --- it uses ->e with 4: int
f(3) # has type bool and uses again ->e with 3: int
(Eq(3))(5) # has type bool because it uses ->e twice
The code for Eq came from this nested logic proof:
| 1. x1 : int assumption
| | 2. x2 : int assumption
| | 3. x1 == x2 : bool algebra 1,3
| 4. def Eqq(x2):return x1 == x2 : int -> bool ->i 2-3
5. def Eq(x1):return Eqq : int -> (int -> bool) ->i 1-4
These ideas are known as the Heyting interpretation of propositional logic --- a proof is a data structure,
and a proposition is "true" if you can build a data value/structure of it.