Skip to content

fix: equal() detects functions in nested containers during equality comparison#959

Closed
He-Pin wants to merge 1 commit into
databricks:masterfrom
He-Pin:fix/equal-function-detection
Closed

fix: equal() detects functions in nested containers during equality comparison#959
He-Pin wants to merge 1 commit into
databricks:masterfrom
He-Pin:fix/equal-function-detection

Conversation

@He-Pin

@He-Pin He-Pin commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Motivation

equal() in Evaluator.scala only rejected function comparison at the top level but missed functions nested inside arrays or objects. All three reference implementations (go-jsonnet, jsonnet-cpp, jrsonnet) agree — when two functions are compared inside any container (array/object), at any nesting depth, the result is an error. When a function is compared against a non-function, the result is false (no error). sjsonnet was incorrectly returning false for function-vs-function in containers.

Modification

  • Array comparison loop: detect Val.Func in either element before recursing, including the shared-thunk fast path (xe eq ye) where forcing a shared lazy thunk may yield a function
  • Object comparison loop: detect Val.Func in field values before recursing via equal()
  • Top-level catch-all: reject when both sides are Val.Func

Result

[function() 3] == [function() 4] and {a: function() 3} == {a: function() 4} now correctly raise "cannot test equality of functions", aligning with all three reference implementations.

Expression go-jsonnet v0.22.0 sjsonnet (before) sjsonnet (after)
f == g ERROR ERROR ERROR
[f] == [g] ERROR false (bug) ERROR
{a: f} == {a: g} ERROR false (bug) ERROR
[[f]] == [[g]] ERROR false (bug) ERROR
[f] == [1] false false false
{a: f} == {a: 1} false false false

Test plan

  • error.equality_function_in_array.jsonnet[f] == [g] raises error
  • error.equality_function_in_object.jsonnet{a: f} == {a: g} raises error
  • equality_function_mixed_container.jsonnet — function vs non-function returns false (no error)
  • Full test suite passes (mill sjsonnet.jvm.3_3_7.test)

References

  • go-jsonnet: builtins.go equal() checks for function type in all comparison paths
  • jsonnet-cpp: vm.cpp equality check rejects function operands at any depth
  • jrsonnet: rejects function equality with "cannot test equality of functions" at all nesting levels

…omparison

Motivation:
The `equal()` function in Evaluator only rejected function comparison at
the top level (`(function() 3) == (function() 3)`) but not when functions
appeared inside arrays or objects. This diverges from go-jsonnet and
jsonnet-cpp which both raise "cannot test equality of functions" when
functions are encountered at any nesting depth.

Modification:
- Array comparison loop: detect Val.Func in either element before
  recursing, including the shared-thunk fast path (xe eq ye)
- Object comparison loop: detect Val.Func in field values before
  recursing via equal()
- Top-level catch-all: reject when both sides are Val.Func

Added golden tests covering functions in arrays, objects, and mixed
function-vs-non-function container comparisons.

Result:
`[function() 3] == [function() 4]` and `{a: function() 3} == {a: function() 4}`
now correctly raise "cannot test equality of functions", aligning with
go-jsonnet and jsonnet-cpp behavior.

References:
- go-jsonnet: builtins.go equal() checks for function type in all paths
- jsonnet-cpp: vm.cpp equality check rejects function operands
@He-Pin

He-Pin commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Closing: PR #958 already handles all nested function equality cases through the top-level case _: Val.Func in equal(), which catches function comparisons at any nesting depth via recursion. I verified this locally: [f] == [g], {a: f} == {a: g}, and [[f]] == [[f]] all correctly raise errors with just PR #958's changes.

The explicit checks in array and object comparison loops added by this PR are redundant — the equal() recursion already routes to the top-level Func case. I've incorporated this PR's test cases (mixed containers, nested array/object errors) into PR #958.

@He-Pin He-Pin closed this Jun 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant