Add a maps extension with maps.merge - #1409
Open
lopster568 wants to merge 1 commit into
Open
Conversation
CEL has no operator for combining two maps: + concatenates strings, bytes, and lists but is not defined for maps, and there is no maps extension. Add maps.merge(a, b), returning a new map with the entries of both and the second argument's values winning on conflicting keys. The merge is shallow, so a value that is itself a map is replaced rather than merged. This is the replace step of the merge semantics discussed in cel-expr#1240; set-if-absent and recursive merge are left for follow-ups. Includes cost estimation and tracking that scale with the combined size of both inputs, registration in the extension option factory and the repl, and documentation in ext/README.md.
lopster568
force-pushed
the
maps-merge-extension
branch
from
August 8, 2026 03:44
51aea9b to
edbceda
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
CEL has no way to combine two maps. The
+operator concatenates strings, bytes, and lists, but has no map overload, and there is nomapsextension.This adds one, with a single function:
It returns a new map holding the entries of both arguments, with the second argument's values winning on conflicting keys. Neither input is modified. The merge is shallow, so a value that is itself a map is replaced rather than merged recursively.
Scope
This is deliberately only the
replacestep from #1240. @TristonianJones suggested starting there:So set-if-absent and recursive merge are left for follow-ups rather than guessed at here.
On the API shape, I went with a bare
maps.merge(a, b)in a newmapsnamespace rather than a single entry point taking a strategy argument, because that is what the existing libraries do:sets.contains/sets.equivalent/sets.intersects,math.ceil/math.floor/math.abs. No function inexttakes a mode or strategy parameter today, so a latermaps.mergeIfAbsentwould fit that pattern without changing this signature. Happy to reshape it if you would rather have something else.Implementation notes
ext/sets.go. Without them the checker would price this as a fixed cost of 1 despite the O(n+m) copy.mapAllocCostis added toext/costs.goalongside the existinglistAllocCost.ext/extension_option_factory.go(factory and alias) and inrepl/evaluator.go.ext/README.mdgains aMapssection in the style of the surrounding entries.Testing
ext/maps_test.gocovers empty inputs, disjoint keys, conflicting keys, nested maps being replaced rather than merged, non-string keys, merging a variable, associativity over disjoint keys, compile-time rejection of non-map arguments, non-map arguments at runtime, and that the cost estimate scales with input size and brackets the tracked cost.go build ./...,go test ./...,go vet ./ext/...andgofmtare clean.