In my previous post I described a simple denotational semantics for the CBV lambda calculus in which the meaning of a λ function is a set of tables. For example, here is a glimpse at some of the tables in the meaning of λx.x+2.
E[|(λx.x+2)|](∅)={∅,{5↦7},{0↦2,1↦3},{0↦2,1↦3,5↦7},⋮}Since then I've been reading the literature starting from an observation by Alan Jeffrey that this semantics seems similar to the domain logic in Abramsky's Ph.D. thesis (1987). That in turn pointed me to the early literature on intersection types, which were invented in the late 1970's by Coppo, Dezani-Ciancaglini, Salle, and Pottinger. It turns out that one of the motivations for intersection types was to create a denotational semantics for the lambda calculus. Furthermore, it seems that intersection types are closely related to my simple denotational semantics!
The intersection types for the pure lambda calculus included function types, intersections, and a top type: A,B,C::=A→B∣A∧B∣⊤
The addition of the singleton number types introduces a choice regarding the top type ⊤. Does it include the numbers and functions or just functions? We shall go with the later, which corresponds to the ν type in the literature (Egidi, Honsell, Rocca 1992).
Now that we have glimpsed the correspondence between tables and intersection types, let's review the typing rules for the implicitly typed lambda calculus with singletons, intersections, and ⊤.
Γ⊢n:nΓ⊢λx.e:⊤(⊤intro)Γ⊢e:AΓ⊢e:BΓ⊢e:A∧B(∧intro)Γ⊢e:AA<:BΓ⊢e:B(Sub)x:A∈ΓΓ⊢x:AΓ,x:A⊢e:BΓ⊢λx.e:A→BΓ⊢e1:A→BΓ⊢e2:AΓ⊢e1e2:B(→elim)With intersection types, one can write the same type in many different ways. For example, the type 5 is the same as 5∧5. One common way to define such equalities is in terms of subtyping: A=B iff A<:B and B<:A.
So how does one define a semantics using intersection types? Barendregt, Coppo, Dezani-Ciancaglini (1983) (BCD) define the meaning of an expression e to be the set of types for which it is typable, something like [|e|](Γ)={A∣Γ⊢e:A}
The next question is, how does the BCD semantics relate to my simple table-based semantics? One difference is that the intersection type system has two rules that are not syntax directed: (∧intro) and (Sub). However, we can get rid of these rules. The (∧intro) rule is not needed for numbers, only for functions. So one should be able to move all uses of the (∧intro) rules to λ's. Γ⊢λx.e:AΓ⊢λx.e:BΓ⊢λx.e:A∧B
All of the rules are now syntax directed, though we now have three rules for λ, but those rules handle the three different possible types for a λ function: A→B, A∧B, and ⊤. Next we observe that a relation is isomorphic to a function that produces a set. So we change from Γ⊢e:A to E[|e|](Γ)=A where A ranges over sets of types, i.e., A∈P(A). We make use of an auxiliary function F to define the meaning of λ functions. E[|n|](Γ)={n}E[|x|](Γ)={Γ(x)}E[|λx.e|](Γ)={A∣F(A,x,e,Γ)}E[|e1e2|](Γ)={B|C∈E[|e1|](Γ)∧A∈E[|e2|](Γ)∧C<:A→B}F(A→B,x,e,Γ)=B∈E[|e|](Γ(x:=A))F(A∧B,x,e,Γ)=F(A,x,e,Γ)∧F(B,x,e,Γ)F(⊤,x,e,Γ)=true
I conjecture that this semantics is equivalent to the "take 3" semantics. There are a couple remaining differences and here's why I don't think they matter. Regarding the case for λ in E, the type A can be viewed as an alternative representation for a table. The function F essentially checks that all entries in the table jive with the meaning of the λ's body, which is what the clause for λ does in the ``take 3'' semantics. Regarding the case for application in E, the C is a table and C<:A→B means that there is some entry A′→B′ in the table C such that A′→B′<:A→B, which means A<:A′ and B′<:B. The A<:A′ corresponds to our use of ⊑ in the "take 3" semantics. The B′<:B doesn't matter.
There's an interesting duality and change of viewpoint going on here between the table-based semantics and the intersection types. The table-based semantics is concerned with what values are produced by a program whereas the intersection type system is concerned with specifying what kind of values are allowed, but the types are so precise that it becomes dual in a strong sense to the values themselves. To make this precise, we can talk about tables in terms of their finite graphs (sets of pairs), and create them using ∅, union, and a singleton input-output pair {(v1,v2)}. With this formulation, tables are literally dual to types, with {(v1,v2)} corresponding to v1→v2, union corresponding to intersection, empty set corresponding to ⊤, and T1⊆T2 corresponding to T2<:T1.
No comments:
Post a Comment