Support ... in tuple patterns - #9
Merged
Merged
Conversation
rodrigues
force-pushed
the
vr/tuple_ellipsis
branch
3 times, most recently
from
July 19, 2026 12:11
f10433a to
ca46efb
Compare
dannote
requested changes
Jul 20, 2026
dannote
left a comment
Collaborator
There was a problem hiding this comment.
Tuple ellipsis looks good overall; there's one regression with quoted ASTs.
| # `normalize_entry/1` — only standalone tuples reach here. | ||
| def normalize({left, right}), | ||
| do: {normalize(left), normalize(right)} | ||
| do: {:{}, nil, [normalize(left), normalize(right)]} |
Collaborator
There was a problem hiding this comment.
This makes pattern normalization and source normalization asymmetric. A plain quoted source tuple stays {a, b}, while the pattern is folded to {:{}, nil, [...]}:
Pattern.match(
quote(do: {:ok, value}),
quote(do: {:ok, _})
)
# :errorCould you normalize both paths the same way (or handle the reverse shape) and add a test with an ordinary quote source?
`{...}`, `{:ok, ...}` and `{..., :done}` matched nothing, and plain `{...}`
even missed 2-tuples like `{:ok, value}` while matching `{}` and `{1, 2, 3}`.
Two causes:
Candidate prefiltering treated `...` and the variadic tuple head `:{}` as
calls, deriving signatures no raw source tuple can satisfy, so find_all
dropped the node before matching. And Elixir encodes 2-tuples as a bare
`{a, b}` but other arities as `{:{}, meta, [elems]}`, so `{...}` (the
variadic form) never reconciled with a literal 2-tuple.
Exclude `:{}` and `...` from signature derivation so tuple patterns become
match-all candidates, like `{_, _}` already was. Normalize genuine 2-tuple
literals into the variadic `{:{}, _, [a, b]}` form so every arity flows
through the one ellipsis path already used by calls and lists.
Map and keyword entries are also `{key, value}` 2-tuples but live inside
lists/maps, so they stay bare and keep matching as before.
Cover every arity and the leading/trailing/both-sides capture forms with
find_all tests, plus a map/keyword regression guard.
A genuine 2-tuple that appears as a call/operator argument or a list
element (e.g. the LHS of `{:ok, _} = _`, `with {:ok, _} <- _`, or
`[{:ok, _}]`) matched nothing.
The source normalizer folds genuine 2-tuples to the variadic
`{:{}, _, [a, b]}` form (Sourceror marks them with a `__block__` wrapper),
but a pattern 2-tuple in that position is indistinguishable from a keyword
entry at parse time, so it stays bare `{a, b}`. `do_match` had no clause to
reconcile the two shapes.
Bridge them in `do_match`: a bare 2-tuple pattern now matches a folded
2-element source tuple.
rodrigues
force-pushed
the
vr/tuple_ellipsis
branch
from
July 21, 2026 13:24
ca46efb to
d25d789
Compare
Pattern.match(quote(do: {:ok, value}), quote(do: {:ok, _})) returned :error.
Standalone tuple patterns fold to the variadic `{:{}, _, [a, b]}` form, and
source normalization folds Sourceror's `{:__block__, _, [{a, b}]}` encoding to
match. But a quoted source has no such wrapper: a genuine 2-tuple stays bare
`{a, b}`, indistinguishable from a keyword/map entry, so the source side can't
fold it and the shapes never reconciled.
Folding bare source tuples isn't an option: find_all visits map/keyword entries
as standalone nodes, so `{...}` would wrongly match `%{a: 1}`. Instead bridge the
reverse shape at match time, mirroring the existing folded-source vs bare-pattern
clause: a bare source 2-tuple now matches a folded pattern tuple.
dannote
approved these changes
Jul 22, 2026
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.
...now works inside tuple patterns, and matches consistently across every tuple arity — including 2-tuples, which were silently skipped:{...}{}and 2-tuples{:ok, ...}:ok{..., :done}:done{a, b, ...}{first, ..., last}...absorbs the middleFixed-length tuple patterns (
{:ok, _},{a, b},{1, 2, 3}) are unchanged: still an exact-length match.Before
Didn't return 2-arity tuples, like
{:ok, _}.After
Map and keyword patterns (%{a: 1}, [a: 1]) are unaffected — their entries are also {key, value} 2-tuples, and they keep matching as before.
Edit: Adding a commit that fixes a case where a 2-tuple sub-pattern matched nothing when it appeared as a call/operator argument or a list element, such as the LHS of
{:ok, _} = _,with {:ok, _} <- _, or[{:ok, _}], because the pattern kept its bare shape while the source folded it; do_match now reconciles the two.