To the language of natural numbers and booleans and functions that we have been developing, we want to add the following:
τ ::= ... | τ x τe1 value e2 value
--------------------- v-pair
<e1,e2> value
e1 -> e'1
--------------------- e-pair1
<e1,e2> -> <e'1,e2>
e1 value
e2 -> e'2
--------------------- e-pair2
<e1,e2> -> <e1,e'2>
e -> e'
--------------------- e-prj1
π1 e -> π1 e'
e -> e'
--------------------- e-prj2
π2 e -> π2 e'
e1 value
e2 value
--------------------- e-prj1-pair
π1 <e1,e2> -> e1
e1 value
e2 value
--------------------- e-prj2-pair
π2 <e1,e2> -> e2
e1 : τ1
e2 : τ2
--------------------- t-pair
<e1,e2> : τ1 x τ2
e : τ1 x τ2
--------------------- t-prj1
π1 e : τ1
e : τ1 x τ2
--------------------- t-prj2
π2 e : τ2
This is the "third case" mentioned before where the term either steps or is a value. In this case, we use the induction hypothesis to see if either subterm of <e1,e2> steps, in which case the expression takes a step - if, on the other hand, they are both values, then the expression as a whole is a value by rule.
A new canonical forms and inversion lemma is needed, but the proof is otherwise of the same form we have done before.
Sums are a little strange to get used to. A metaphor that might work would be to think about IP packets. Packets are some data, packaged up with some data that tells you what that data is. Imagine we had some function DEAL_WITH_IPV4(packet) that understands how to interpret an IP version 4 packet, and some other function DEAL_WITH_IPV6(packet) that understands how to interpret an IP version 6 packet. The type of an "incoming packet" is then EITHER a IPv4 packet or an IPv6 packet --- if we have a type IPv4 and a type IPv6, then our internet packets have type IPv4 + IPv6.
If we want to package some IPv4 packet P4 to send it over the wire, then we would write inl P4, which is typed according to the following rule:
P4 : IPv4
---------------------
inl P4 : IPv4 + IPv6
If we recieve a packet over the wire and we want to process it, then we need to analyze the packet and then send it off to the correct function. We would do this by a switch statement in a language like C and by case analysis in a language like ML:
case incoming_packet of
inl pack4 => DEAL_WITH_IPV4(pack4)
inr pack6 => DEAL_WITH_IPV6(pack6)
We will write this as case(incoming_packet, pack4. DEAL_WITH_IPV4(pack4), pack6. DEAL_WITH_IPV6(pack6) in the syntax of the class:
In general, we want to add these to our langauge:
τ ::= ... | τ + τ
e ::= ... | inl e | inr e | case(e, x.e1, x.e2)
e value
--------------------- v-inl
inl e value
e value
--------------------- v-inr
inr e value
e -> e'
--------------------- e-inl
inl e -> inl e'
e -> e'
--------------------- e-inr
inr e -> inr e'
e -> e'
------------------------------------------ e-case
case(e,x.e1,x.e2) -> case(e',x.e1,x.e2)
e value
------------------------------------------ e-case-inl
case(inl e,x.e1,x.e2) -> [e/x]e1
e value
------------------------------------------ e-case-inr
case(inr e,x.e1,x.e2) -> [e/x]e2
e : τ1
--------------------- t-inl
inl e : τ1 + τ2
e : τ2
--------------------- t-inr
inr e : τ1 + τ2
e : τ1 + τ2
x : τ1 |- e1 : τ
x : τ2 |- e2 : τ
------------------------- t-case
case(e,x.e1,x.e2) : τ