5.3. Lambdas and function values
-
CoreCpp.Typing.accept[complete] -
CoreCpp.Typing.lambdaAt[complete] -
CoreCpp.TBind[complete] -
CoreCpp.TEnv.captured[complete]
A lambda has no type of its own. The judgment \Gamma \vdash e \lhd \tau, e is acceptable at \tau, checks a lambda against the type of the library expected where it occurs, a type to which its function type converts, std::function in Definition 8.2.2, as argument, as initialiser of a declaration and as return expression, and for any other expression it is \Gamma \vdash e : \tau' with \tau' \approx \tau.
The body is checked under \Gamma with every variable of the enclosing scope marked read only, the copies of [=], and with the parameters of the lambda as ordinary variables.
The rule T-LocVar requires a variable that is not read only, so an assignment to a captured variable, or a reference to it, is a type error.
\dfrac{\Gamma \vdash e : \tau' \qquad \tau' \approx \tau \qquad \tau' \text{ has values}}{\Gamma \vdash e \lhd \tau}\;\textsf{(Accept)}
\dfrac{\begin{array}{c} \Gamma' = \Gamma \text{ marked read only}, [x_1 \mapsto \tau_1, \ldots, x_k \mapsto \tau_k]\qquad \Gamma' \vdash c \dashv \Gamma'' \\ \tau, \tau_i \text{ storable and well formed}\qquad \Gamma \vdash_L \tau(\tau_1, \ldots, \tau_k) \hookrightarrow L\langle\bar{\tau}\rangle \end{array}}{\Gamma \vdash \mathtt{[=]}(\tau_1\,x_1, \ldots, \tau_k\,x_k)\ \mathtt{->}\ \tau\ \{c\} \lhd L\langle\bar{\tau}\rangle}\;\textsf{(T-Lambda)}
Outside its three positions a lambda is a type error, and auto x = [=]… is a syntax error, because the grammar admits a lambda only as an argument expression.
Some of the implementations are partial, so Lean records opaque constants that carry the type and not the body, and no property of them is proved here.
Lean code for Definition5.3.1●4 definitions
Associated Lean declarations
-
CoreCpp.Typing.accept[complete]
-
CoreCpp.Typing.lambdaAt[complete]
-
CoreCpp.TBind[complete]
-
CoreCpp.TEnv.captured[complete]
-
CoreCpp.Typing.accept[complete] -
CoreCpp.Typing.lambdaAt[complete] -
CoreCpp.TBind[complete] -
CoreCpp.TEnv.captured[complete]
-
opaquedefined in CoreCpp/Typing.leancomplete
opaque CoreCpp.Typing.accept (p : CoreCpp.Program) (Γ : CoreCpp.TEnv) (context : String) (e : CoreCpp.Expr) (τ : CoreCpp.Ty) : CoreCpp.T Unit
opaque CoreCpp.Typing.accept (p : CoreCpp.Program) (Γ : CoreCpp.TEnv) (context : String) (e : CoreCpp.Expr) (τ : CoreCpp.Ty) : CoreCpp.T Unit
Γ ⊢ e ◁ τ, e is acceptable at type τ. For a lambda, `lambdaAt`. For any other expression, Γ ⊢ e : τ' with τ' ≈ τ and τ' with values. ``` Γ ⊢ e : τ' τ' ≈ τ τ' has values ───────────────────────────────────── (Accept) Γ ⊢ e ◁ τ ```
-
opaquedefined in CoreCpp/Typing.leancomplete
opaque CoreCpp.Typing.lambdaAt (p : CoreCpp.Program) (Γ : CoreCpp.TEnv) (ps : List CoreCpp.Param) (r : CoreCpp.Ty) (b : List CoreCpp.Cmd) (τ : CoreCpp.Ty) : CoreCpp.T Unit
opaque CoreCpp.Typing.lambdaAt (p : CoreCpp.Program) (Γ : CoreCpp.TEnv) (ps : List CoreCpp.Param) (r : CoreCpp.Ty) (b : List CoreCpp.Cmd) (τ : CoreCpp.Ty) : CoreCpp.T Unit
Γ ⊢ [=](τ₁ x₁, …, τₖ xₖ) -> τ { c } ◁ L⟨τ̄⟩, a lambda where a type of the library to which its function type converts is expected. ``` Γ' = Γ marked read only, [x₁ ↦ τ₁, …, xₖ ↦ τₖ] Γ' ⊢ c ⊣ Γ'' τ, τᵢ storable, well formed Γ ⊢_L τ(τ₁, …, τₖ) ↪ L⟨τ̄⟩ ──────────────────────────────────────────────────────────── (T-Lambda) Γ ⊢ [=](τ₁ x₁, …, τₖ xₖ) -> τ { c } ◁ L⟨τ̄⟩ ``` The lambda has the function type τ(τ₁, …, τₖ), and the conversion is a judgement of the library, the one of `std::function` for instance. The body is checked under Γ with every variable of the enclosing scope marked read only, the copies of `[=]`, and with the parameters of the lambda as ordinary variables. -
structuredefined in CoreCpp/Typing.leancomplete
structure CoreCpp.TBind : Type
structure CoreCpp.TBind : Type
A binding of Γ. `const` marks a variable captured by copy in a lambda body, which the body may read and not write.
Fields
ty : CoreCpp.Ty
const : Bool
-
defdefined in CoreCpp/Typing.leancomplete
def CoreCpp.TEnv.captured (Γ : CoreCpp.TEnv) : CoreCpp.TEnv
def CoreCpp.TEnv.captured (Γ : CoreCpp.TEnv) : CoreCpp.TEnv
Γ with every binding marked read only, the context of a lambda body.
-
CoreCpp.Eval.expr[complete] -
CoreCpp.Eval.captures[complete] -
CoreCpp.Expr.vars[complete] -
CoreCpp.Cmd.vars[complete]
A lambda evaluates to a closure with copies of the free variables of its body that the environment binds, taken when the lambda is evaluated. The closure holds values, not locations. A captured int or bool is a copy the body reads and never writes, and a captured pointer still reaches its object in \sigma, so an effect through it is visible outside the lambda.
\dfrac{\begin{array}{c} \{y_1, \ldots, y_m\} = \text{free variables of } c \text{ bound in } \rho, \text{ minus the } x_i\qquad \rho(y_j) = \ell_j \\ \ell_j \in \mathrm{dom}\,\sigma\qquad w_j = \sigma(\ell_j) \end{array}}{\begin{array}{c} \rho, \sigma \vdash \mathtt{[=]}(\tau_1\,x_1, \ldots, \tau_k\,x_k)\ \mathtt{->}\ \tau\ \{c\} \\ \Rightarrow \mathsf{closure}(\vec{x}, \tau, c, [y_1 \mapsto w_1, \ldots, y_m \mapsto w_m]), \sigma \end{array}}\;\textsf{(Lambda)}
Some of the implementations are partial, so Lean records opaque constants that carry the type and not the body, and no property of them is proved here.
Lean code for Definition5.3.2●4 definitions
Associated Lean declarations
-
CoreCpp.Eval.expr[complete]
-
CoreCpp.Eval.captures[complete]
-
CoreCpp.Expr.vars[complete]
-
CoreCpp.Cmd.vars[complete]
-
CoreCpp.Eval.expr[complete] -
CoreCpp.Eval.captures[complete] -
CoreCpp.Expr.vars[complete] -
CoreCpp.Cmd.vars[complete]
-
opaquedefined in CoreCpp/Eval.leancomplete
opaque CoreCpp.Eval.expr (fs : CoreCpp.FunEnv) (ρ : CoreCpp.Env) (σ : CoreCpp.Store) (e : CoreCpp.Expr) : CoreCpp.M (CoreCpp.Val × CoreCpp.Store)
opaque CoreCpp.Eval.expr (fs : CoreCpp.FunEnv) (ρ : CoreCpp.Env) (σ : CoreCpp.Store) (e : CoreCpp.Expr) : CoreCpp.M (CoreCpp.Val × CoreCpp.Store)
ρ, σ ⊢ e ⇒ v, σ'
-
defdefined in CoreCpp/Eval.leancomplete
def CoreCpp.Eval.captures (ρ : CoreCpp.Env) (σ : CoreCpp.Store) (ps : List CoreCpp.Param) (b : List CoreCpp.Cmd) : CoreCpp.M (List (String × CoreCpp.Val))
def CoreCpp.Eval.captures (ρ : CoreCpp.Env) (σ : CoreCpp.Store) (ps : List CoreCpp.Param) (b : List CoreCpp.Cmd) : CoreCpp.M (List (String × CoreCpp.Val))
The free variables of a lambda body that the enclosing environment binds, each with its current value, the captures of `[=]`. Reading a captured variable whose location left the store is error.
-
opaquedefined in CoreCpp/Syntax.leancomplete
opaque CoreCpp.Expr.vars : CoreCpp.Expr → List String
opaque CoreCpp.Expr.vars : CoreCpp.Expr → List String
The variables an expression mentions, the candidates for capture by a lambda. A nested lambda contributes its own free variables, its parameters excluded.
-
opaquedefined in CoreCpp/Syntax.leancomplete
opaque CoreCpp.Cmd.vars : CoreCpp.Cmd → List String
opaque CoreCpp.Cmd.vars : CoreCpp.Cmd → List String
-
CoreCpp.Typing.callValue[complete] -
CoreCpp.Eval.applyClosure[complete] -
CoreCpp.Eval.applyVals[complete]
A call through a function value evaluates the function expression to a closure, the arguments by value and left to right, allocates fresh locations for the captured copies and for the parameters, runs the body in an environment with those bindings only, and frees them on return.
When the callee is a variable f of function type, f(…) is this rule and not the call of the function named f.
The type checker rewrites the call into the use operator() of Definition 8.1.2. The rule F-Call of Definition 8.2.2 then takes the application of the closure to the values, the premises of CallFn after the arguments, as its premise \mathrm{apply}.
\dfrac{\begin{array}{c} \Gamma \vdash e : L\langle\bar{\tau}\rangle\qquad \Gamma \vdash_L \mathtt{operator()} : \tau_1 \times \cdots \times \tau_k \to \tau \\ \Gamma \vdash e_i \lhd \tau_i \quad (1 \le i \le k) \end{array}}{\Gamma \vdash e(e_1, \ldots, e_k) : \tau}\;\textsf{(T-CallFn)}
\dfrac{\begin{array}{c} \rho, \sigma \vdash e \Rightarrow \mathsf{closure}(x_1 \ldots x_k, \tau, c, [y_1 \mapsto w_1, \ldots, y_m \mapsto w_m]), \sigma_0 \\ \rho, \sigma_{i-1} \vdash e_i \Rightarrow v_i, \sigma_i \quad (1 \le i \le k)\qquad (\ell'_j, \cdot) = \mathrm{alloc}(w_j) \\ (\ell_i, \cdot) = \mathrm{alloc}(v_i) \\ [y_1 \mapsto \ell'_1, \ldots, y_m \mapsto \ell'_m, x_1 \mapsto \ell_1, \ldots, x_k \mapsto \ell_k], \sigma' \vdash c \Rightarrow \mathsf{ret}\,v, \rho'', \sigma'' \end{array}}{\rho, \sigma \vdash e(e_1, \ldots, e_k) \Rightarrow v, \sigma'' \setminus (\{\ell'_j, \ell_i\} \cup (\rho'' \setminus \rho_c))}\;\textsf{(CallFn)}
With \mathsf{normal} in place of \mathsf{ret}\,v the result is \mathsf{void} if \tau = \mathsf{void} and error otherwise. Nothing of the environment of the call is visible inside the body, only the copies and the parameters.
The application of the closure to the values is a judgment of its own, the premise the rule F-Call of Definition 8.2.2 names \mathrm{apply}. A value that is not a closure is error.
\dfrac{\begin{array}{c} v = \mathsf{closure}(x_1 \ldots x_k, \tau, c, [y_1 \mapsto w_1, \ldots, y_m \mapsto w_m]) \\ (\ell'_j, \cdot) = \mathrm{alloc}(w_j) \qquad (\ell_i, \cdot) = \mathrm{alloc}(v_i) \\ \rho_c = [y_1 \mapsto \ell'_1, \ldots, y_m \mapsto \ell'_m, x_1 \mapsto \ell_1, \ldots, x_k \mapsto \ell_k] \\ \rho_c, \sigma' \vdash c \Rightarrow \mathsf{ret}\,v', \rho'', \sigma'' \end{array}}{\mathrm{apply}\ v\ (v_1, \ldots, v_k) \Rightarrow v', \sigma'' \setminus (\{\ell'_j, \ell_i\} \cup (\rho'' \setminus \rho_c))}\;\textsf{(Apply)}
The implementations are partial, so Lean records opaque constants that carry the type and not the body, and no property of them is proved here.
Lean code for Definition5.3.3●3 definitions
Associated Lean declarations
-
CoreCpp.Typing.callValue[complete]
-
CoreCpp.Eval.applyClosure[complete]
-
CoreCpp.Eval.applyVals[complete]
-
CoreCpp.Typing.callValue[complete] -
CoreCpp.Eval.applyClosure[complete] -
CoreCpp.Eval.applyVals[complete]
-
opaquedefined in CoreCpp/Typing.leancomplete
opaque CoreCpp.Typing.callValue (p : CoreCpp.Program) (Γ : CoreCpp.TEnv) (fe : CoreCpp.Expr) (t : CoreCpp.Ty) (es : List CoreCpp.Expr) : CoreCpp.T CoreCpp.Ty
opaque CoreCpp.Typing.callValue (p : CoreCpp.Program) (Γ : CoreCpp.TEnv) (fe : CoreCpp.Expr) (t : CoreCpp.Ty) (es : List CoreCpp.Expr) : CoreCpp.T CoreCpp.Ty
The call of a value of type t with the arguments es, shared by `T-CallFn` on a variable and on any other expression. The value has a type of the library with a member `operator()`, a `std::function` for instance. ``` Γ ⊢ e : L⟨τ̄⟩ Γ ⊢_L operator() : τ₁ × … × τₖ → τ Γ ⊢ eᵢ ◁ τᵢ ──────────────────────────────────────────────────────────── (T-CallFn) Γ ⊢ e(e₁, …, eₖ) : τ ```
-
opaquedefined in CoreCpp/Eval.leancomplete
opaque CoreCpp.Eval.applyClosure (fs : CoreCpp.FunEnv) (ρ : CoreCpp.Env) (σ : CoreCpp.Store) (v : CoreCpp.Val) (es : List CoreCpp.Expr) : CoreCpp.M (CoreCpp.Val × CoreCpp.Store)
opaque CoreCpp.Eval.applyClosure (fs : CoreCpp.FunEnv) (ρ : CoreCpp.Env) (σ : CoreCpp.Store) (v : CoreCpp.Val) (es : List CoreCpp.Expr) : CoreCpp.M (CoreCpp.Val × CoreCpp.Store)
The application of a closure to arguments. ``` v = closure(x₁ … xₖ, τ, c, [y₁ ↦ w₁, …, yₘ ↦ wₘ]) ρ, σ ⊢ e₁ ⇒ v₁, σ₁ … ρ, σₖ₋₁ ⊢ eₖ ⇒ vₖ, σₖ (ℓ'ⱼ, ·) = alloc wⱼ (ℓᵢ, ·) = alloc vᵢ ρ_c = [y₁ ↦ ℓ'₁, …, yₘ ↦ ℓ'ₘ, x₁ ↦ ℓ₁, …, xₖ ↦ ℓₖ] ρ_c, σ' ⊢ c ⇒ ret v, ρ'', σ'' ─────────────────────────────────────────────────────────────── (Apply) apply v (e₁, …, eₖ) ⇒ v, σ'' ∖ ({ℓ'ⱼ, ℓᵢ} ∪ (ρ'' ∖ ρ_c)) ``` The arguments evaluate left to right, by value. The locations ℓ'ⱼ and ℓᵢ are fresh, for the copies and for the parameters. The environment ρ_c holds the closure environment and nothing else is visible. On the way out the copies, the parameters and the locals leave the store. With normal in place of ret v the result is void if τ = void and error (missing return) otherwise. A value that is not a closure is error. -
opaquedefined in CoreCpp/Eval.leancomplete
opaque CoreCpp.Eval.applyVals (fs : CoreCpp.FunEnv) (σ : CoreCpp.Store) (v : CoreCpp.Val) (vs : List CoreCpp.Val) : CoreCpp.M (CoreCpp.Val × CoreCpp.Store)
opaque CoreCpp.Eval.applyVals (fs : CoreCpp.FunEnv) (σ : CoreCpp.Store) (v : CoreCpp.Val) (vs : List CoreCpp.Val) : CoreCpp.M (CoreCpp.Val × CoreCpp.Store)
The application of a closure to values, the judgement the library uses through `apply`.