Loading [MathJax]/jax/output/HTML-CSS/jax.js

Friday, March 10, 2017

The Take 3 Semantics, Revisited

In my post about intersection types as denotations, I conjectured that the simple "take 3" denotational semantics is equivalent to an intersection type system. I haven't settled that question per se, but I've done something just as good, which is to show that everything that I've done with the intersection type system can also be done with the "take 3" semantics (with a minor modification).

Recall that the main difference between the "take 3" semantics and the intersection type system is how subsumption of functions is handled. The "take 3" semantics defined function application as follows, using the subset operator to require the argument v2 to include all the entries in the parameter v2, while allowing v2 to have possibly more entries. E[|e1e2|](ρ)={v3|v1v2v2.v1E[|e1|](ρ)v2E[|e2|](ρ){v2v3}v1v2v2} Values are either numbers or functions. Functions are represented as a finite tables mapping values to values. tablesT::={v1v1,,vnvn}valuesv::=nT and is defined as equality on numbers and subset for function tables: nnT1T2T1T2 Recall that is defined in terms of equality on elements.

In an intersection type system (without subsumption), function application uses subtyping. Here's one way to formulate the typing rule for application: Γ2e1:CΓ2e2:AC<:ABA<:AΓ2e1e2:B Types are defined as follows typesA,B,C::=nABAB and the subtyping relation is given below. n<:n(a)<:(b)AB<:(c)A<:AB<:BAB<:AB(d)C<:AC<:BC<:AB(e)AB<:A(f)AB<:B(g)(CA)(CB)<:C(AB)(h) Recall that values and types are isomorphic (and dual) to eachother in this setting. Here's the functions T and V that map back and forth between values and types. T(n)=nT({v1v1,,vnvn})=T(v1)T(v1)T(vn)T(vn)V(n)=nV(AB)={V(A)V(B)}V(AB)=V(A)V(B)V()=

Given that values and types are really the same, the the typing rule for application is almost the same as the equation for the denotation of E[|e1e2|](ρ). The only real difference is the use of <: versus . However, subtyping is a larger relation than , i.e., v1v2 implies T(v1)<:T(v2) but it is not the case that A<:B implies V(A)V(B). Subtyping is larger because of rules (d) and (h). The other rules just express the dual of .

So the natural question is whether subtyping needs to be bigger than , or would we get by with just ? In my last post, I mentioned that rule (h) was not necessary. Indeed, I removed it from the Isabelle formalization without disturbing the proofs of whole-program soundness and completeness wrt. operational semantics, and was able to carry on and prove soundness wrt. contextual equivalence. This morning I also replaced rule (d) with a rule that only allows equal function types to be subtypes. AB<:AB(d) The proofs went through again! Though I did have to make two minor changes in the type system without subsumption to ensure that it stays equivalent to the version of the type system with subsumption. I used the rule given above for function application instead of Γ2e1:CΓ2e2:AC<:ABΓ2e1e2:B Also, I had to change the typing rule for λ to use subtyping to relate the body's type to the return type. Γ,x:Ae:BB<:BΓλx.e:AB Transposing this back into the land of denotational semantics and values, we get the following equation for the meaning of λ, in which everything in the return specification v2 must be contained in the value v2 produced by the body. E[|λx.e|](ρ)={v|v1v2.{v1v2}vv2.v2E[|e|](ρ(x:=v1))v2v2}

So with this little change, the "take 3" semantics is a great semantics for the call-by-value untyped lambda calculus! For whole programs, it's sound and complete with respect to the standard operational semantics, and it is also sound with respect to contextual equivalence.

No comments:

Post a Comment