Skip to content

Lambda and Variable Expressions and BoundExpressions - #9257

Draft
mhk197 wants to merge 4 commits into
mk/bound-expression-enumfrom
mk/lambda-variables
Draft

Lambda and Variable Expressions and BoundExpressions#9257
mhk197 wants to merge 4 commits into
mk/bound-expression-enumfrom
mk/lambda-variables

Conversation

@mhk197

@mhk197 mhk197 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Adds lambdas and variables as Expression Variants and implements the scope machinery to bind them.

/// A name bound in a scope.
pub struct Variable(Arc<str>);

/// A body evaluated under a frame binding `params`.
pub struct Lambda { params: Box<[Variable]>, body: Arc<Expression> }

pub enum Expression {
    Scalar { scalar_fn: ScalarFnRef, children: Arc<Vec<Expression>> },
    Root,
    Variable(Variable),
    Lambda(Lambda),
}

pub struct Scope { root: DType, frames: Vec<Frame> }   // Frame: [(Variable, DType)]

Binding:

expr.bind_scope(&scope)                      // errors on a lambda in a value position
lambda.bind(&scope, param_dtypes)            // -> BoundLambda
scope.push_frame(frame)                      // a caller can bind names without any binder node

Design notes

A lambda is bound by whoever knows its parameter types. They come from whatever applies it, so
Lambda::bind takes them. That is the entry point a higher-order function will call with types
derived from its own arguments; here, tests call it directly.

Scope carries frames, not a flat map. A flat name -> dtype map cannot distinguish "this
binder's own parameter" from "a name from an enclosing binder", which is what a capture check needs.
resolve returns the dtype and the frame depth so that check is later a comparison rather than a
re-derivation.

@codspeed-hq

codspeed-hq Bot commented Aug 7, 2026

Copy link
Copy Markdown

Merging this PR will regress 1 benchmark

⚡ 3 improved benchmarks
❌ 1 regressed benchmark
✅ 1925 untouched benchmarks
⏩ 51 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation chunked_dict_primitive_canonical_into[f32, (1000, 100, 100)] 1.5 ms 1.6 ms -11.15%
Simulation take_map[(0.1, 0.5)] 2.5 ms 2.2 ms +14.29%
Simulation take_map[(0.1, 1.0)] 4 ms 3.6 ms +10.91%
Simulation chunked_varbin_opt_to_varbinview_builder[(10, 100)] 842.9 µs 764.8 µs +10.21%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing mk/lambda-variables (8e710f5) with mk/bound-expression-enum (9296dac)

Open in CodSpeed

Footnotes

  1. 51 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Signed-off-by: Matt Katz <mhkatz97@gmail.com>
@mhk197
mhk197 force-pushed the mk/lambda-variables branch from e41b484 to c077107 Compare August 7, 2026 03:06
@mhk197 mhk197 added the changelog/feature A new feature label Aug 7, 2026
@mhk197 mhk197 changed the title lambdas and variables Lambda and Variable Expressions and BoundExpressions Aug 7, 2026
mhk197 added 3 commits August 6, 2026 21:46
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
…ialization docs

Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Variable and Lambda get reserved expression ids alongside Root, so the
unbound tree stays fully serializable. Variable carries its name in
VariableOpts; Lambda carries its params in LambdaOpts and its body as its
single proto child. All three reserved ids resolve before the registry
lookup.

Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog/feature A new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant