Core C++

5.3. Lambdas and function values🔗

Definition5.3.1
Group: Functions, parameters and calls. (9)
Group member previews
Preview
Definition 5.1.1
Loading preview
Group member preview content is loaded from the rendered-fragment cache.
Statement uses 3
Statement dependency previews
Preview
Definition 2.1.9
Loading preview
Statement dependency preview content is loaded from the rendered-fragment cache.
Used by 2
Reverse dependency previews
Preview
Definition 5.3.2
Loading preview
Reverse dependency preview content is loaded from the rendered-fragment cache.
✓L∃∀N

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
  • opaquedefined in CoreCpp/Typing.lean
    complete
    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.lean
    complete
    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.
    
  • structure(2 fields)defined in CoreCpp/Typing.lean
    complete
    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. 
    ty : CoreCpp.Ty
    const : Bool
  • defdefined in CoreCpp/Typing.lean
    complete
    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. 
Definition5.3.2
Group: Functions, parameters and calls. (9)
Group member previews
Preview
Definition 5.1.1
Loading preview
Group member preview content is loaded from the rendered-fragment cache.
Statement uses 3
Statement dependency previews
Preview
Definition 2.1.6
Loading preview
Statement dependency preview content is loaded from the rendered-fragment cache.
used by 1✓L∃∀N

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
  • opaquedefined in CoreCpp/Eval.lean
    complete
    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.lean
    complete
    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.lean
    complete
    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.lean
    complete
    opaque CoreCpp.Cmd.vars : CoreCpp.Cmd → List String
    opaque CoreCpp.Cmd.vars :
      CoreCpp.Cmd → List String
Definition5.3.3
Group: Functions, parameters and calls. (9)
Group member previews
Preview
Definition 5.1.1
Loading preview
Group member preview content is loaded from the rendered-fragment cache.
Statement uses 4
Statement dependency previews
Preview
Definition 2.1.5
Loading preview
Statement dependency preview content is loaded from the rendered-fragment cache.
used by 1✓L∃∀N

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
  • opaquedefined in CoreCpp/Typing.lean
    complete
    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.lean
    complete
    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.lean
    complete
    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`.