Values vs. Computations. We now turn to Call-by-push-value (Levy 2004). This language (Table 5) distinguishes between values and computations, with value types represented by A, \(A'\), etc., and computation types by \(\underline{B}\), \(\underline{B'}\), etc. The set of closed values of type A will be represented by \(\text {Vals}^A\); that of closed computations by \(\text {Comps}^{\underline{B}}\). Variables always have value type. Here we include value products and sums, products of computation types
, types \(F A\) for computations aiming to return a value, and functions which in CBPV are computations taking values to computations. Central to CBPV, we also include value types \(U \underline{B}\) of suspended computations of type \(\underline{B}\)—which can be of one of two forms.
Table 5. Call-by-push with recursion-value—syntax
Recursion. In addition to the usual \(\mathop {\mathbf {thunk}}\)s of computations, we also have recursively defined thunks \(\mathbf {threc}\, x . t\). An alternative would be to use recursive computations \(\varGamma \vdash ^c \mathop {\mathbf {rec}}\nolimits x . t : \underline{B}\). Although the two are equivalent via the definitions \( \mathop {\mathbf {rec}}\nolimits x . t \mathrel {:=} \mathop {\mathbf {force}}\mathbf {threc}\, x . t \) and \( \mathbf {threc}\, x . t \mathrel {:=} \mathop {\mathbf {thunk}}\mathop {\mathbf {rec}}\nolimits x . t \), there are two reasons for preferring \(\mathbf {threc}\): One is that, in some denotational models (e.g. state or continuation passing), \(\mathbf {threc}\) has a simpler denotation than \(\mathop {\mathbf {rec}}\nolimits \). The other is that a treatment based on \(\mathbf {threc}\) would be more easily adapted to call-by-value, where recursion and lambda are combined.
Table 6. Call-by-push-value with recursion—reduction
Evaluation. Evaluation (Table 6) pertains only to computations. To those on the co-domain side of the evaluation relation \(\mathbin {\Downarrow }\), we call the terminal computations or, alternatively, the normal forms; and their (typed-indexed) set is represented by \(\text {NFs}^{\underline{B}}\). Since we have two forms of thunked computations, the action of forcing one such into execution much act accordingly; this \(\mathop { unthunk }\)ing (a derived operation on the syntax) returns the computations suspended inside \(\mathop {\mathbf {thunk}}\)s, or plucks out the computation from a
suitably instantiated by the recursive thunk itself—i. e.
. Note that reduction is deterministic.
4.1 Theory
Theory. By a (CBPV) congruence on closed terms we mean a type-indexed equivalence relation \(\approx \) on closed values and computations such that for all closed terms \(t \approx t'\) and (value or computation) context \(C[-]\) we have \(C[t] \approx C[t']\), respectively. A congruence is a rationally continuous \(\beta \)-\(\varOmega \)-fix theory when it satisfies the rules in Table 7. Rational chains are now those built by the application of a context \(C[-]\) to the (thunked) approximants \(\mathbf {threc}^n\) of recursive thunks and which are defined by the clauses
and
; continuity is defined accordingly. Any congruence including the \(\beta \) and fixpoint rules is easily seen to be sound. We shall show that with the remaining rules it is also adequate.
Table 7. Call-by-push-value with recursion—rationally continuous \(\beta \)-\(\varOmega \)-fix theory
4.2 Adequacy
Values: Empty Shells. In the proof of adequacy for PCF with sums we were required to introduce the tests so that we could, metaphorically, peek inside the injections and transform the rational chains there into equivalent ones with the properties we needed (cf. proof of Proposition 6). Here the problem expands to all value types. When checking rational admissibility, we need to decompose a value into its ultimate pattern and its constituent thunks (Lassen and Levy 2008, following ideas from Abramsky and McCusker 1997; also discernible in the work of Zeilberger 2008) and use those to find equivalent chains that can be used to establish adequacy.
Definition 4
(Ultimate Patterns). The set of of ultimate patterns \(\text {UP}^{A}\) for a value type A is given by induction on the following rules: \(-_{U \underline{B}} \in \text {UP}^{U \underline{B}}\), \(\left\langle \right\rangle \in \text {UP}^1\) and
For a given ultimate pattern \(p \in \text {UP}^A\) the finite sequence of hole-types in pattern p is given by induction by
$$\begin{aligned} H(-_{U \underline{B}})&= (U \underline{B})&H(\left\langle \right\rangle )&= \epsilon&H(\left\langle p , p' \right\rangle )&= H(p)\mathbin {++}H(p')\\ H(\mathop {\mathbf {inl}}\nolimits p)&= H(p)&H(\mathop {\mathbf {inr}}\nolimits p)&= H(p)\end{aligned}$$
Proposition 7
(Value Decomposition). Given \(\vdash ^v v : A\), there is a unique \(p \in \text {UP}^A\) and a unique sequence \((\vdash ^v v_i : H(p)_i)_{i < |H(p)|} \)—the filling—for which \(v = p \,@\, {(v_i)_{i < |H(p)|}} \), using the reassembly function
Tests. Ultimate patterns let us define the tests that extract the computations embedded in a given value. Like in the PCF sum case, we can use them to define values that are equivalent to a given one but make use only of the latter. If the values are derived from some family of contexts for the holes, then we can derive an equivalent context from the respective ultimate pattern.
Definition 5
For \(p \in \text {UP}^A\), and \( i < |H(p)|\), we define a context \(\mathop {\mathcal T}\nolimits ^{p }_{i}[-]\) by induction on \(p \in \text {UP}^A\) using the rules below. Note that when \(\varGamma \vdash ^v - : A\) the test has type \(\varGamma \vdash ^c \mathop {\mathcal T}\nolimits ^{p }_{i}[-] : \underline{B_i}\) where \(U \underline{B_i}= H(p)_i\).
$$\begin{aligned} \mathop {\mathcal T}\nolimits ^{-_{U B} }_0[-]&= \mathop {\mathbf {force}}-\\ \mathop {\mathcal T}\nolimits ^{\mathop {\mathbf {inl}}\nolimits p }_{i} [-]&= \mathbf {match}\,- \,\mathbf {as}\,\left\{ \mathop {\mathbf {inl}}\nolimits x . \mathop {\mathcal T}\nolimits ^{p }_{i}[x],\ \mathop {\mathbf {inr}}\nolimits y . \varOmega \right\} \\ \mathop {\mathcal T}\nolimits ^{\mathop {\mathbf {inr}}\nolimits p }_{i} [-]&= \mathbf {match}\,- \,\mathbf {as}\,\left\{ \mathop {\mathbf {inl}}\nolimits x . \varOmega ,\ \mathop {\mathbf {inr}}\nolimits y .\mathop {\mathcal T}\nolimits ^{p }_{i}[y] \right\} \\ \mathop {\mathcal T}\nolimits ^{\left\langle p , p' \right\rangle }_{i< |H(p)|}[-]&= \mathbf {match} - \mathbf {as}<{x , y}>.\, {\mathop {\mathcal T}\nolimits ^{p }_i [ x ]} \\ \mathop {\mathcal T}\nolimits ^{\left\langle p , p' \right\rangle }_{i=|H(p)| + i'}[-]&= \mathbf {match} - \mathbf {as} <{x , y}>.\, {\mathop {\mathcal T}\nolimits ^{p' }_{i'} [ y ]} \end{aligned}$$
Proposition 8
(Tests Decompose). Given a pattern \(p \in \text {UP}^A\), a sequence \((\vdash w_i : H(p)_i)_{i < |H(p)|}\), and \( i < |H(p)|\), we have \( \mathop {\mathcal T}\nolimits ^{p }_{i}[{p}\,@\,{(w_i)}_{i < |H(p)|}] \approx \mathop {\mathbf {force}}w_i \).
Proposition 9
For \(\vdash ^c t : F A\) , and \(p \in \text {UP}^{A}\), if \(t \approx \mathop {\mathbf {return\,}}p\,@\,{(v_i)_{i < |H(p)|}}\) then, successively:
-
1.
\( \forall i < |H(p)| . \mathop {\mathbf {thunk}}(t \, \mathbin {\mathbf {to}} \,x.\, \mathop {\mathcal T}\nolimits ^{p }_i[x]) \approx v_i \)
-
2.
\( p\,@\,{(v_i)_{i< |H(p)|}} \approx p\,@\,{(\mathop {\mathbf {thunk}}(t \, \mathbin {\mathbf {to}} \, x.\, \mathop {\mathcal T}\nolimits ^{p }_i[x]))_{i < |H(p)|}} \)
-
3.
\( t \approx \mathop {\mathbf {return\,}}p\,@\,{(\mathop {\mathbf {thunk}}(t\, \mathbin {\mathbf {to}} \, x.\, \mathop {\mathcal T}\nolimits ^{p }_i[x]))_{i < |H(p)|}} \)
Approximation Candidates. Unlike PCF where we have computations and normal forms, CBPV has three levels of syntax: values, terminals, and computations. For the purposes of defining the needed approximation candidates, terminals (read: normal forms) and computations, behave like their PCF counterparts and have (now) familiar definitions of approximation candidates. Approximation candidates for value types enforce that: only structurally similar values are related; that they are (left) closed under equivalence of their holes; and that they are closed under the usual chains.
Definition 6
(Approximation Candidates). Given a value type A, an approximation candidate \(\mathrel {\triangleleft }\) for A is a subset of \(\text {Vals}^A \times \text {Vals}^A\) such that
-
1.
Structural Matching: \( p \,@\,{(v_i)_i} \mathrel {\triangleleft }p'\,@\,{(w_i)_i} \implies p = p'\)
-
2.
Computational \(\approx \) Extension: if \( p \,@\, (v'_i)_{i< |H(p)|} \mathrel {\triangleleft }p \,@\,{(w_i)_{i < |H(p)|}} \) then
$$(\forall i< |H(p)| . v_i \approx v'_i) \implies p \,@\, (v_i)_{i< |H(p)|} \mathrel {\triangleleft }p \,@\, (w_i)_{i < |H(p)|} $$
-
3.
Rational Admissibility: for \(x : U \underline{B}\vdash ^c t :\underline{B}\)
Given a computation type \(\underline{B}\), an approximation candidate \(\mathrel {\triangleleft }\) for \(\underline{B}\) is a subset of \(\text {Comps}^{\underline{B}} \times \text {NF}^{\underline{B}}\) such that
-
1.
\(\approx \) Extension: \( t \approx t' \text { and }t' \mathrel {\triangleleft }r \implies t \mathrel {\triangleleft }r \)
-
2.
Rational Admissibility: for \(x : U \underline{B}\vdash ^c t : \underline{B}\)
Proposition 10
Given a (computation) approximation candidate \(\mathrel {\triangleleft }\) on \(\underline{B}\), define its closure as the binary relation \(\text {Comps}^{\underline{B}} \times \text {Comps}^{\underline{B}}\) where
$$ t \mathrel {\triangleleft }^c u \iff t \approx \varOmega \text { or }(\exists r . u \mathbin {\Downarrow }r \text { and }t \mathrel {\triangleleft }r)$$
It satisfies the following properties:
-
1.
\(\varOmega \) Property: \( \varOmega \mathrel {\triangleleft }^c u \) for any \(u \in \text {Comps}^{\underline{B}}\)
-
2.
\(\approx \) Extension: \( t \approx t' \text { and }t' \mathrel {\triangleleft }^c u \implies t \mathrel {\triangleleft }^c u \)
-
3.
\(\mathbin {\Downarrow }\) Extension: \( t \mathrel {\triangleleft }^c u' \text { and }(\forall r . u' \mathbin {\Downarrow }r \implies u \mathbin {\Downarrow }r) \implies t \mathrel {\triangleleft }^c u \)
-
4.
Rational Admissibility: for \(x : U \underline{B}\vdash ^c t : \underline{B}\)
Actions. We can then define the actions on these approximation candidates associated with each type constructor. Mostly this is done by structure (for values) or by use (for computations); the exceptions are \(U \) types and \(F \) types that we define, respectively, by structure, and by use. Note that it is the existential quantification in the definition of the \(F \) action that—very much like PCF sums—requires the use of the tests. Using them, we can easily define, by induction, the approximation relation and thereby establish the adequacy of the theory.
Proposition 11
(Thunk Action). Let \(\mathrel {\triangleleft }\) be an approximation candidate for \(\underline{B}\). Then the binary relation
$$ v \mathrel {\left\langle U (\mathrel {\triangleleft })\right\rangle }w \iff \mathop {\mathbf {force}}v \mathrel {\triangleleft }^c \mathop { unthunk }w $$
is an approximation candidate for \(U \underline{B}\).
Proposition 12
(F Action). Let \(\mathrel {\triangleleft }\) be an approximation candidate for A. Then the following is an approximation candidate for \(F A\):
$$ t \mathrel {\left\langle F (\mathrel {\triangleleft })\right\rangle }\mathop {\mathbf {return\,}}w \iff \exists v \mathrel {\triangleleft }w. t \approx \mathop {\mathbf {return\,}}v $$
Definition 7
(Enviroments). Given a typing context \(\varGamma \), an environment \(\sigma \) for \(\varGamma \) is a substitution that maps each \(x : A \in \varGamma \) to a closed term of type \(\vdash ^v \sigma (x) : A\). If \(\sigma _1\) and \(\sigma _2\) are two such, we write \(\sigma _1 \mathrel {\triangleleft }_\varGamma \sigma _2\) to mean \( \sigma _1(x) \mathrel {\triangleleft }_{A} \sigma _2(x) \) for all \(x : A \in \varGamma \).
Proposition 13
For any \(\varGamma \vdash ^c t : B\) (resp. \(\varGamma \vdash ^v v : A\)), and environments \(\sigma _1 \mathrel {\triangleleft }_\varGamma \sigma _2\) we have \( t[\sigma _1] \mathrel {\triangleleft }_{\underline{B}}^c t[\sigma _2]\) (resp. \(v[\sigma _1] \mathrel {\triangleleft }_A v[\sigma _2]\)).
Corollary 2
(Adequacy). For any computation \(\vdash ^c t : \underline{B}\), if \( t \Uparrow \) then \( t \approx \varOmega \).