Class: Rigor::Type::Refined
- Inherits:
-
Object
- Object
- Rigor::Type::Refined
- Includes:
- AcceptanceRouter, ValueSemantics
- Defined in:
- lib/rigor/type/refined.rb,
sig/rigor/type.rbs
Overview
Refined[base, predicate_id] — predicate-subset half of the OQ3 refinement-carrier strategy
(ADR-3, Working Decision Option C). Sibling of
Type::Difference, which carries the point-removal half.
lowercase-string = Refined[Nominal[String], :lowercase]
uppercase-string = Refined[Nominal[String], :uppercase]
numeric-string = Refined[Nominal[String], :numeric]
The carrier wraps a base type and a predicate_id Symbol drawn from PREDICATES. The recogniser
is invoked at constant-fold and acceptance time over a Constant<base> value; for non-Constant
receivers the carrier is a marker the catalog tier consults to project String#downcase /
String#upcase (etc.) into the matching refinement.
Display routes through CANONICAL_NAMES: registered (base_class_name, predicate_id) pairs print
in their kebab-case spelling (lowercase-string); unregistered combinations fall back to the
base & predicate? operator form per type-operators.md.
Construction MUST go through Type::Combinator.refined / the per-name factories
(Combinator.lowercase_string, Combinator.uppercase_string, Combinator.numeric_string). Direct
.new is an internal escape hatch for tests and combinator's own implementation.
Constant Summary collapse
- PREDICATES =
{ lowercase: ->(v) { v.is_a?(String) && v == v.downcase }, not_lowercase: ->(v) { v.is_a?(String) && v != v.downcase }, uppercase: ->(v) { v.is_a?(String) && v == v.upcase }, not_uppercase: ->(v) { v.is_a?(String) && v != v.upcase }, numeric: ->(v) { ruby_numeric_literal?(v) }, not_numeric: ->(v) { v.is_a?(String) && !ruby_numeric_literal?(v) }, decimal_int: ->(v) { v.is_a?(String) && DECIMAL_INT_STRING_PATTERN.match?(v) }, octal_int: ->(v) { v.is_a?(String) && OCTAL_INT_STRING_PATTERN.match?(v) }, hex_int: ->(v) { v.is_a?(String) && HEX_INT_STRING_PATTERN.match?(v) }, # `literal-string` is a flow-tracked predicate, not a value-level predicate: a String is # literal-string when it is known to come from a source-code literal (or composition of # literals). Every concrete `Constant<String>` is already literal by construction, so the # inspection recogniser returns true for any String — the property is really tracked in the flow # analysis (interpolation, concatenation, RBS::Extended `return: literal-string`) rather than # recovered by inspecting an arbitrary string. literal_string: ->(v) { v.is_a?(String) } }.freeze
Instance Attribute Summary collapse
-
#base ⇒ Type::t
readonly
Returns the value of attribute base.
-
#predicate_id ⇒ Symbol
readonly
Returns the value of attribute predicate_id.
Class Method Summary collapse
-
.ruby_numeric_literal?(value) ⇒ Boolean
True when
valueis a String that is a single, complete Ruby numeric literal.
Instance Method Summary collapse
- #== ⇒ Boolean
- #accepts ⇒ AcceptsResult
- #bot ⇒ Trinary
-
#complement_predicate_id ⇒ Symbol?
The registered complement predicate id, or nil when no pair is registered for this predicate.
- #describe(verbosity = :short) ⇒ String
- #dynamic ⇒ Trinary
-
#erase_to_rbs ⇒ String
Erases to the base nominal: every refinement MUST erase to its base per
rbs-erasure.md. - #hash ⇒ Integer
-
#initialize(base, predicate_id) ⇒ Refined
constructor
A new instance of Refined.
- #inspect ⇒ String
-
#matches?(value) ⇒ Boolean
Recognises a Ruby value against this carrier's predicate.
- #top ⇒ Trinary
Methods included from ValueSemantics
Constructor Details
#initialize(base, predicate_id) ⇒ Refined
Returns a new instance of Refined.
34 35 36 37 38 39 40 |
# File 'lib/rigor/type/refined.rb', line 34 def initialize(base, predicate_id) raise ArgumentError, "predicate_id must be a Symbol" unless predicate_id.is_a?(Symbol) @base = base @predicate_id = predicate_id freeze end |
Instance Attribute Details
#base ⇒ Type::t (readonly)
Returns the value of attribute base.
32 33 34 |
# File 'lib/rigor/type/refined.rb', line 32 def base @base end |
#predicate_id ⇒ Symbol (readonly)
Returns the value of attribute predicate_id.
32 33 34 |
# File 'lib/rigor/type/refined.rb', line 32 def predicate_id @predicate_id end |
Class Method Details
.ruby_numeric_literal?(value) ⇒ Boolean
Returns true when value is a String that is a
single, complete Ruby numeric literal. Total over
arbitrary input — never raises (Prism reports malformed
input through errors, it does not throw).
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/rigor/type/refined.rb', line 144 def self.ruby_numeric_literal?(value) return false unless value.is_a?(String) return false if value.empty? # A numeric literal carries no whitespace; reject any leading / trailing / interior space so the # *whole* string must be the literal (Prism would otherwise accept a trailing-space `"1 "`). return false if value.match?(/\s/) return false unless value.match?(NUMERIC_LITERAL_PREFIX) result = Prism.parse(value) return false unless result.errors.empty? body = result.value.statements&.body return false unless body && body.size == 1 node = body.first NUMERIC_LITERAL_NODES.any? { |klass| node.is_a?(klass) } end |
Instance Method Details
#== ⇒ Boolean
177 |
# File 'sig/rigor/type.rbs', line 177
def ==: (untyped other) -> bool
|
#accepts ⇒ AcceptsResult
175 |
# File 'sig/rigor/type.rbs', line 175
def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult
|
#complement_predicate_id ⇒ Symbol?
Returns the registered complement predicate id, or nil when no pair is registered for this predicate.
222 223 224 |
# File 'lib/rigor/type/refined.rb', line 222 def complement_predicate_id COMPLEMENT_PAIRS[predicate_id] end |
#describe(verbosity = :short) ⇒ String
42 43 44 45 46 47 |
# File 'lib/rigor/type/refined.rb', line 42 def describe(verbosity = :short) named = canonical_name return named if named "#{base.describe(verbosity)} & #{predicate_id}?" end |
#dynamic ⇒ Trinary
63 64 65 |
# File 'lib/rigor/type/refined.rb', line 63 def dynamic base.respond_to?(:dynamic) ? base.dynamic : Trinary.no end |
#erase_to_rbs ⇒ String
Erases to the base nominal: every refinement MUST erase to its base per
rbs-erasure.md.
51 52 53 |
# File 'lib/rigor/type/refined.rb', line 51 def erase_to_rbs base.erase_to_rbs end |
#hash ⇒ Integer
178 |
# File 'sig/rigor/type.rbs', line 178
def hash: () -> Integer
|
#inspect ⇒ String
73 74 75 |
# File 'lib/rigor/type/refined.rb', line 73 def inspect "#<Rigor::Type::Refined #{describe(:short)}>" end |
#matches?(value) ⇒ Boolean
Recognises a Ruby value against this carrier's predicate. The trinary return is intentional:
true / false when the predicate registry decides, nil when the predicate is unknown to the
registry, so callers (today Inference::Acceptance) can fall through to gradual-mode :maybe.
rubocop:disable Style/ReturnNilInPredicateMethodDefinition
81 82 83 84 85 86 |
# File 'lib/rigor/type/refined.rb', line 81 def matches?(value) recogniser = PREDICATES[predicate_id] return nil if recogniser.nil? !!recogniser.call(value) end |