Class: Rigor::Type::Union
- Inherits:
-
Object
- Object
- Rigor::Type::Union
- Includes:
- AcceptanceRouter, ValueSemantics
- Defined in:
- lib/rigor/type/union.rb,
sig/rigor/type.rbs
Overview
A normalized non-empty union of two or more distinct types. Unions are constructed exclusively through Rigor::Type::Combinator.union, which flattens nested unions, deduplicates structurally-equal members, and collapses single-member or empty results to the appropriate scalar type. Direct calls to .new are an internal contract: callers MUST pass an already-normalized members array.
See docs/type-specification/normalization.md.
Instance Attribute Summary collapse
-
#members ⇒ Array[Type::t]
readonly
Returns the value of attribute members.
Instance Method Summary collapse
- #== ⇒ Boolean
- #accepts ⇒ AcceptsResult
- #bot ⇒ Trinary
-
#describe(verbosity = :short) ⇒ String
Display-only adoption of two concise RBS spellings for the union (see docs/type-specification/normalization.md § "Interaction with display" and rbs-compatible-types.md § "Optionals").
- #dynamic ⇒ Trinary
-
#erase_to_rbs ⇒ String
ADR-1 § "RBS round-trip is lossless" + the value-lattice rule
untyped | T = untyped(everyTis gradually consistent withuntyped). - #hash ⇒ Integer
-
#initialize(members) ⇒ Union
constructor
A new instance of Union.
- #inspect ⇒ String
- #top ⇒ Trinary
Methods included from ValueSemantics
Constructor Details
#initialize(members) ⇒ Union
Returns a new instance of Union.
19 20 21 22 23 24 25 26 |
# File 'lib/rigor/type/union.rb', line 19 def initialize(members) unless members.is_a?(Array) && members.size >= 2 raise ArgumentError, "Union requires at least two members; use Combinator.union for normalization" end @members = members.freeze freeze end |
Instance Attribute Details
#members ⇒ Array[Type::t] (readonly)
Returns the value of attribute members.
17 18 19 |
# File 'lib/rigor/type/union.rb', line 17 def members @members end |
Instance Method Details
#== ⇒ Boolean
146 |
# File 'sig/rigor/type.rbs', line 146
def ==: (untyped other) -> bool
|
#accepts ⇒ AcceptsResult
145 |
# File 'sig/rigor/type.rbs', line 145
def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult
|
#describe(verbosity = :short) ⇒ String
Display-only adoption of two concise RBS spellings for the union (see
docs/type-specification/normalization.md § "Interaction with display" and rbs-compatible-types.md
§ "Optionals"). Both are purely cosmetic: @members keeps every carrier verbatim, so the
underlying type identity, RBS erasure, and round-trip are unchanged — only the human-facing
rendering reads like the RBS the user wrote.
* `true | false` → `bool` (the RBS boolean alias). The `bool` token leads the
rendering, so `false | Foo | true` reads as `bool | Foo` rather than burying the pair
mid-list.
* `T | nil` → `T?` (the RBS optional sugar). Only applied when exactly one
*logical* member remains beside `nil`, matching the rbs gem's own `to_s`: a multi-member
union such as `Integer | String | nil` stays explicit rather than gaining a parenthesised
`(Integer | String)?`. The two collapses compose, so `false | true | nil` reads as `bool?`.
41 42 43 44 45 46 47 48 49 50 |
# File 'lib/rigor/type/union.rb', line 41 def describe(verbosity = :short) return "#{optional_inner(verbosity)}?" if optional? if boolean_pair? rest = members.reject { |m| boolean_literal?(m) } ["bool", *rest.map { |m| m.describe(verbosity) }].join(" | ") else members.map { |m| m.describe(verbosity) }.join(" | ") end end |
#dynamic ⇒ Trinary
77 78 79 |
# File 'lib/rigor/type/union.rb', line 77 def dynamic members.any? { |m| m.respond_to?(:dynamic) && m.dynamic.yes? } ? Trinary.maybe : Trinary.no end |
#erase_to_rbs ⇒ String
ADR-1 § "RBS round-trip is lossless" + the value-lattice rule untyped | T = untyped (every T
is gradually consistent with untyped). When any union member erases to "untyped", the whole
union erases to "untyped" — the RBS surface has no carrier for "Dynamic-origin alongside a
static facet", and the gradual-consistency contract guarantees the substitution is sound at every
call site.
Post-erasure dedupe removes String | String artefacts that arise when two structurally-distinct
Constant carriers (e.g. Constant<"Alice"> / Constant<"Bob">) share an RBS-erased envelope.
The members themselves are already structurally deduped at construction by
Type::Combinator.union, but the post-erase strings can collide.
62 63 64 65 66 67 |
# File 'lib/rigor/type/union.rb', line 62 def erase_to_rbs erased = members.map(&:erase_to_rbs) return "untyped" if erased.include?("untyped") erased.uniq.join(" | ") end |
#hash ⇒ Integer
147 |
# File 'sig/rigor/type.rbs', line 147
def hash: () -> Integer
|
#inspect ⇒ String
87 88 89 |
# File 'lib/rigor/type/union.rb', line 87 def inspect "#<Rigor::Type::Union #{describe(:short)}>" end |