"[A | B]" grouped-union syntax for type tags - #1702
Closed
apiology wants to merge 1 commit into
Closed
Conversation
Closes lsegal#1699: there was no way to nest a union inside a position where "," already means something else - most concretely, an order-dependent list's positional slots ("Array(A, B)" already means "2 slots", so "," can't also mean "alternative for this slot" there). "[...]" is a new, dedicated grouping construct - unlike "<...>", "(...)", and "{...}", it never takes a preceding type name and never means a collection; "[A | B]" alone just means "A or B", exactly like the plain top-level list "A, B", but usable anywhere a type is expected. "|" is only meaningful inside "[...]", and "," is not allowed inside "[...]" - the two never compete for the same position, so unlike YARD's overloaded "," there is nothing to disambiguate. This resolves the motivating example from the issue directly, no extra nesting required: "Array([Integer | String], Symbol)" is a 2-element tuple whose first element is an Integer or a String. Also documents three pre-existing (but previously undocumented in docs/Tags.md) anonymous shorthand forms - "<A>", "(A)", and "{A=>B}" - where the leading type name can be omitted and defaults to "Array" or "Hash" respectively. These were already implemented and spec-tested, and documented externally at https://yardoc.org/types.html, but absent from this repo's own docs.
apiology
added a commit
to apiology/yard
that referenced
this pull request
Jul 31, 2026
Combines intersection type support ("A & B", from lsegal#1700) with grouped-
union support ("[A | B]", from lsegal#1702) into a single PR, per review
feedback, and fixes an integration bug the merge surfaced: the
grouping syntax's "|" handler pushed its type directly instead of
routing through finish_intersection, which would have silently
dropped any pending "&" conjuncts when a group boundary was hit (e.g.
in "[Foo & Bar | Baz]").
Also fixes a real English-rendering ambiguity in the combined output:
GroupType wraps its whole member list in one pair of parens, but
IntersectionType (and a multi-method DuckType, "#foo & #bar") render
as a bare "X and Y" with no punctuation of their own. Sitting next to
a sibling in the group's "or"-joined list, that read ambiguously
("a Foo and a Bar or a Baz" doesn't show which operator binds
tighter) even though the parse itself was correct. GroupType now adds
defensive parens around exactly those two cases.
Documents operator precedence explicitly: "&" always binds tighter
than whichever separator surrounds it ("," at the top level, "|"
inside "[...]"); "|" is only valid inside "[...]"; "," is never valid
inside "[...]". Adds end-to-end specs combining both operators,
including the precedence and disambiguation cases above.
2 tasks
Contributor
Author
|
Superseded by #1700, which now combines this PR's grouped-union work with the intersection-type PR into a single PR, per review feedback. Closing this one. |
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.
Summary
Closes #1699. There was no way to nest a union inside a position where
,already means something else - most concretely, an order-dependent list's
positional slots (
Array(A, B)already means "2 slots", so,can't alsomean "alternative for this slot" there).
[...]is a new, dedicated grouping construct - unlike<...>,(...),and
{...}, it never takes a preceding type name and never means acollection;
[A | B]alone just means "A or B", exactly like the plaintop-level list
A, B, but usable anywhere a type is expected.|is onlymeaningful inside
[...], and,is not allowed inside[...]- the twonever compete for the same position, so there's nothing to disambiguate
(no need for a "cannot mix
,and|" rule, or extra nested parens).This resolves the motivating example from the issue directly:
Array([Integer | String], Symbol)is a 2-element tuple whose firstelement is an
Integeror aString, followed by aSymbol- no extranesting required.
Why
[]and not()or reusing,()already has an established meaning (Array(A, B), anorder-dependent tuple) that this repo isn't changing here, to leave that
syntax free for a possible future use.
[has no meaning at all today inYARD::Tags::TypesExplainer::Parser(only
<,(,{are wired up as bracket-openers there), even thoughthe outer tag-value tokenizer (
DefaultFactory::TYPELIST_OPENING_CHARS)already balances it generically - so it's free to claim without any
parsing-level conflict.
,-for-union convention inside the groupingbrackets was considered and rejected: it would make
[A, B]and[A | B]two spellings of the same thing, and (worse) invitesmisreading
[A, B]as an array/tuple literal by analogy with otherecosystems, rather than as a plain 2-way alternative.
|avoids both.What else changed
Also documents three pre-existing (but previously undocumented in
docs/Tags.md) anonymous shorthand forms -<A>,(A), and{A=>B}-where the leading type name can be omitted and defaults to
ArrayorHashrespectively. These were already implemented and spec-tested, anddocumented externally at https://yardoc.org/types.html, but absent from
this repo's own docs; see #1701.
Test plan
bundle exec rspec spec/tags/types_explainer_spec.rb- added specsfor
GroupType#to_s, parser-level grouping/error cases, and end-to-end.explainexamples.bundle exec rspec- full suite green (2802 examples, 0 failures).