Call-by-push-value
Template:Short description In programming language theory, call-by-push-value (CBPV) is an intermediate language that embeds the call-by-value (CBV) and call-by-name (CBN) evaluation strategies. CBPV is structured as a polarized λ-calculus with two main types, "values" (+) and "computations" (-).<ref name="Kavvos2020">Template:Cite journal</ref> Restrictions on interactions between the two types enforce a controlled order of evaluation, similar to monads or CPS. The calculus can embed computational effects, such as nontermination, mutable state, or nondeterminism. There are natural semantics-preserving translations from CBV and CBN into CBPV. This means that giving a CBPV semantics and proving its properties implicitly establishes CBV and CBN semantics and properties as well. Paul Blain Levy formulated and developed CBPV in several papers and his doctoral thesis.<ref name="Levy99">Template:Cite conference</ref><ref name="Levy2003">Template:Cite book</ref><ref name="Levy2022">Template:Cite journal</ref>
DefinitionEdit
The CBPV paradigm is based on the slogan "a value is, a computation does". One complication in the presentation is distinguishing type variables ranging over value types from those ranging over computation types. This article follows Levy in using underlines to denote computations, so <math>B</math> is an (arbitrary) value type but <math>\underline{B}</math> is a computation type.<ref name="Levy2022"/> Some authors use other conventions, such as distinct sets of letters.<ref>Template:Cite journal</ref>
The exact set of constructs varies by author and desired use for the calculus, but the following constructs are typical:<ref name="Levy99"/><ref name="Levy2022"/>
- Lambdas
λx.M
are computations of type <math>A \to \underline{B}</math>, where <math>x : A</math> and <math>M : \underline{B}</math>. A lambda applicationF V
orV'F
is a computation of type <math>\underline{B}</math>, where <math>V : A</math> and <math>F : A \to \underline{B}</math>. The let-binding constructlet { x_1 = V_1; ... }. M
binds valuesx_1
to valuesV_1
, of matching types <math>A_1</math>, inside a computationM
: <math>\underline{B}</math>. - A thunk
thunk M
is a value of type <math>U \underline{A}</math> constructed from a computationM
of type <math>\underline{A}</math>. Forcing a thunk is a computation,force X
: <math>\underline{A}</math> for a thunkX
: <math>U \underline{A}</math>. - It is also possible to wrap a value
V
of type <math>A</math> as a computationreturn V
: <math>F A</math>. Such a computation can be used inside another computation asM to x. N
: <math>\underline{B}</math>, whereM
: <math>F A</math>, andN
: <math>\underline{B}</math> is a computation. - Values can also include algebraic data types constructed from a tag and zero or more sub-values, while computations include a deconstructing pattern-match
match V as { (1,...) in M_1, ... }
. Depending on presentation, ADTs may be limited to binary sums and products, Booleans only, or be omitted altogether.
A program is a closed computation of type <math>F A</math>, where <math>A</math> is a ground ADT type.<ref name="Levy2022"/>
Complex valuesEdit
Expressions such as not true : bool
make sense denotationally. But, following the rules above, not
can only be encoded using pattern-matching, which would make it a computation, and therefore the overall expression must also be a computation, giving not true : F bool
. Similarly, there is no way to obtain 1
from (1,2)
without constructing a computation. When modelling CBPV in the equational or category theory, such constructs are indispensable. Levy therefore defines an extended IR, "CBPV with complex values". This IR extends let-binding to bind values within a value expression, and also to pattern-match a value with each clause returning a value expression.<ref name="Levy2003"/> Besides modelling, such constructs also make writing programs in CBPV more natural.<ref name="Levy99"/>
Complex values complicate the operational semantics, in particular requiring an arbitrary decision of when to evaluate the complex value. Such a decision has no semantic significance because evaluating complex values has no side effects. Also, it is possible to syntactically convert any computation or closed expression to one of the same type and denotation without complex values.<ref name="Levy2003"/> Therefore, many presentations omit complex values.<ref name="Levy2022"/>
TranslationEdit
The CBV translation produces CBPV values for each expression. A CBV function λx.M
: <math>A \to_v B</math> is translated to thunk λx.Mv
: <math>U (A^v \to F B^v)</math>. A CBV application M N
: <math>A</math> is translated to a computation Mv to f in Nv to x in x'(force f)
of type <math>F A</math>, making the order of evaluation explicit. A pattern match match V as { (1,...) in M_1, ... }
is translated as Vv to z in match z as { (1,...) in M_1v, ... }
. Values are wrapped with return
when necessary, but otherwise remain unmodified.<ref name="Levy99"/> In some translations, sequencing may be required, such as translating inl M
to M to x. return inl x
.<ref name="Levy2022"/>
The CBN translation produces CBPV computations for each expression. A CBN function λx.M
: <math>A \to B</math> translates unaltered, λx.MN
: <math>(U A^n) \to X^n</math>. A CBN application M N
: <math>C</math> is translated to a computation Mv (thunk Nv)
of type <math>C^n</math>. A pattern match match V as { (1,...) in M_1, ... }
is translated similarly to CBN as Vn to z in match z as { (1,...) in M_1n, ... }
. ADT values are wrapped with return
, but force
and thunk
are also necessary on internal structure. Levy's translation assumes that M = force (thunk M)
, which does indeed hold.<ref name="Levy99"/>
It is also possible to extend CBPV to model call-by-need, by introducing a M need x. N
construct that allows visible sharing. This construct has semantics similar to M name x. N = (λy.N[x ↦ (force y)])(thunk M)
, except that with the need
construct, the thunk of M
is evaluated at most once.<ref>Template:Cite book</ref>
ModificationsEdit
Some authors have noted that CBPV can be simplified, by removing either the U type constructor (thunks)<ref name="Egger2014">Template:Cite journal</ref> or the F type constructor (computations returning values).<ref name="Ehrhard2016">Template:Cite book</ref> Egger and Mogelberg justify omitting U on the grounds of streamlined syntax and avoiding the clutter of inferable conversions from computations to values. This choice makes computation types a subset of value types, and it is then natural to expand function types to a full function space between values. They term their calculus the "Enriched Effects Calculus". This modified calculus is equivalent to a superset of CBPV via a bidirectional semantics-preserving translation.<ref name="Egger2014"/> Ehrhard in contrast omits the F type constructor, making values a subset of computations. Ehrhard renames computations to "general types" to better reflect their semantics. This modified calculus, the "half-polarized lambda calculus", has close connections to linear logic.<ref name="Ehrhard2016"/><ref>Template:Cite book</ref> It can be translated bidirectionally to a subset of a fully-polarized variant of CBPV.<ref>Template:Citation</ref>