Bug: Lambda evaluation only computes last field when depending on local variables
Disclaimer: This issue is the result of human-AI collaboration after searching for the bug cause . I indeed experienced and reproduced the error and provided the code(partially the file is long and contains bunch of colors). The analysis down below was primarily generated by an LLM (DeepSeek) and is speculative - the author does not fully understand the codebase and this analysis may be incorrect. Please verify independently.
Description
When defining a lambda that creates a config object with multiple computed fields depending on local variables (defined in the same lambda), only the last field in declaration order appears in the output. Earlier fields are missing entirely.
This works correctly in global scope but fails in lambda/local scope.
Reproduction
# some types and schemas like Palette
...
# Base data structure (validated as correct YAML was indeed output)
everforest_src = {
light = {
fg = {...},
bg = {
hard = {...},
..other variants
}
},
dark = {..similar to light mode}
} # Schema-validated theme, no errors
# ✅ WORKS - Global scope
_mode = "light"
_variant = "hard"
everforest_light_hard = {
mode = _mode
variant = _variant
fg = everforest_src[mode].fg
bg = everforest_src[mode].bg[variant]
}
# Result: {mode="light", variant="hard", fg=..., bg=...} - both fields present
# ❌ FAILS - Lambda scope (fg first, bg last)
get_palette2 = lambda _src, _mode, _variant -> {
mode = _mode
variant = _variant
fg = everforest_src[mode].fg
bg = everforest_src[mode].bg[variant]
}
everforest_light_hard2 = get_palette2(everforest_src, "light", "hard")
# Result: {mode="light", variant="hard", bg=...} - fg missing entirely
# ❌ FAILS - Lambda scope (bg first, fg last)
get_palette3 = lambda _src, _mode, _variant -> {
mode = _mode
variant = _variant
bg = everforest_src[mode].bg[variant]
fg = everforest_src[mode].fg
}
everforest_light_hard3 = get_palette3(everforest_src, "light", "hard")
# Result: {mode="light", variant="hard", fg=...} - bg missing entirely
# ❌ STILL FAILS - Even with intermediate variable
get_palette4 = lambda _src, _mode, _variant -> {
mode = _mode
variant = _variant
mode_obj = _src[_mode]
bg = mode_obj.bg[_variant]
fg = mode_obj.fg
}
everforest_light_hard4 = get_palette4(everforest_src, "light", "hard")
# Result: Still only the last field appears (fg or bg depending on order, here bg missing)
Expected Behavior
All fields in the returned config should be present regardless of their declaration order. Both fg and bg should appear in all lambda examples.
Actual Behavior
Only the field that appears last in the lambda body appears in the output. The field(s) before it are completely missing.
Environment
- KCL version: 0.12.3-linux-amd64 (installed via
go install)
- OS: Debian 13
Analysis Context
The issue was investigated by examining the KCL repository structure. Starting from compiler base(apprently empty), the investigation focused on the evaluator crate, particularly the interaction between scope handling (scope.rs), lazy evaluation (lazy.rs), and execution context (context.rs).
Speculative Analysis
Disclaimer: The following analysis was AI-generated and may be incorrect.(i don't fully understand this). It's provided as a starting point for maintainers who know the codebase.
The issue appears to stem from how KCL handles variables in lambda scopes versus global scope. In global scope, variables are properly evaluated through the lazy evaluation system. In lambda scope, variables seem to be tracked in two parallel systems that don't synchronize correctly.
When a lambda executes:
- Local variables (
_mode, _variant) are stored in the current Scope via add_variable()
- Fields (
fg, bg) have setters recorded in LazyEvalScope via emit_setters()
- When accessing variables during computation, lambda uses direct scope lookup (
get_variable()) which bypasses lazy evaluation(calls undefined value instead of actually triggering evaluation)
- When constructing the final result, it uses
get_value_from_lazy_scope() which only fully evaluates the last accessed field due to backtracking logic
This creates a race condition where earlier fields never get properly forced through the lazy evaluation system, causing them to be missing from the output.
The key files that may contain the issue:
scope.rs: Variable resolution and lambda scope handling
lazy.rs: Lazy evaluation and backtracking mechanism
context.rs: Lambda context management
Possible Impact
This affects any KCL code that uses lambdas to generate config objects with multiple computed fields - a common pattern for configuration generation, theming, and reusable logic.
Bug: Lambda evaluation only computes last field when depending on local variables
Disclaimer: This issue is the result of human-AI collaboration after searching for the bug cause . I indeed experienced and reproduced the error and provided the code(partially the file is long and contains bunch of colors). The analysis down below was primarily generated by an LLM (DeepSeek) and is speculative - the author does not fully understand the codebase and this analysis may be incorrect. Please verify independently.
Description
When defining a lambda that creates a config object with multiple computed fields depending on local variables (defined in the same lambda), only the last field in declaration order appears in the output. Earlier fields are missing entirely.
This works correctly in global scope but fails in lambda/local scope.
Reproduction
Expected Behavior
All fields in the returned config should be present regardless of their declaration order. Both
fgandbgshould appear in all lambda examples.Actual Behavior
Only the field that appears last in the lambda body appears in the output. The field(s) before it are completely missing.
Environment
go install)Analysis Context
The issue was investigated by examining the KCL repository structure. Starting from compiler base(apprently empty), the investigation focused on the evaluator crate, particularly the interaction between scope handling (
scope.rs), lazy evaluation (lazy.rs), and execution context (context.rs).Speculative Analysis
Disclaimer: The following analysis was AI-generated and may be incorrect.(i don't fully understand this). It's provided as a starting point for maintainers who know the codebase.
The issue appears to stem from how KCL handles variables in lambda scopes versus global scope. In global scope, variables are properly evaluated through the lazy evaluation system. In lambda scope, variables seem to be tracked in two parallel systems that don't synchronize correctly.
When a lambda executes:
_mode,_variant) are stored in the currentScopeviaadd_variable()fg,bg) have setters recorded inLazyEvalScopeviaemit_setters()get_variable()) which bypasses lazy evaluation(calls undefined value instead of actually triggering evaluation)get_value_from_lazy_scope()which only fully evaluates the last accessed field due to backtracking logicThis creates a race condition where earlier fields never get properly forced through the lazy evaluation system, causing them to be missing from the output.
The key files that may contain the issue:
scope.rs: Variable resolution and lambda scope handlinglazy.rs: Lazy evaluation and backtracking mechanismcontext.rs: Lambda context managementPossible Impact
This affects any KCL code that uses lambdas to generate config objects with multiple computed fields - a common pattern for configuration generation, theming, and reusable logic.