Class: Rigor::Type::Constant

Inherits:
Object
  • Object
show all
Includes:
AcceptanceRouter, PlainLattice
Defined in:
lib/rigor/type/constant.rb,
sig/rigor/type.rbs

Overview

A literal carrier under ADR-3 OQ1 Option C (Hybrid). Wraps a Ruby literal value of one of the supported immutable-ish classes. Compound literal shapes (Tuple, HashShape, Record) get dedicated classes in later slices; Range is carried only when both static endpoints are known enough for tuple slicing.

See docs/adr/4-type-inference-engine.md for the tentative answer to the open question and docs/type-specification/rigor-extensions.md for the refinement neighbourhood this carrier lives in.

Constant Summary collapse

SCALAR_CLASSES =
[
  Integer,
  Float,
  String,
  Symbol,
  Range,
  Rational,
  Complex,
  Regexp,
  Pathname,
  ::Set,
  # `Date` covers `DateTime` (a subclass). `Time` is core. Both arise only from deterministic
  # constructor folding (`Date.new` / `Time.utc`) — there is no Date / Time literal node — so a
  # `Constant` carrier is always sound.
  Date,
  Time,
  TrueClass,
  FalseClass,
  NilClass
].freeze
RBS_LITERAL_CLASSES =
{
  TrueClass => "true",
  FalseClass => "false",
  NilClass => "nil"
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value) ⇒ Constant

Returns a new instance of Constant.

Parameters:

  • value (Object)


47
48
49
50
51
52
53
54
# File 'lib/rigor/type/constant.rb', line 47

def initialize(value)
  unless SCALAR_CLASSES.any? { |klass| value.is_a?(klass) }
    raise ArgumentError, "Rigor::Type::Constant only carries scalar literals; got #{value.class}"
  end

  @value = freezable_carrier?(value) ? value.dup.freeze : value
  freeze
end

Instance Attribute Details

#valueObject (readonly)

Returns the value of attribute value.

Returns:

  • (Object)


45
46
47
# File 'lib/rigor/type/constant.rb', line 45

def value
  @value
end

Instance Method Details

#==(other) ⇒ Boolean Also known as: eql?

Parameters:

  • other (Object)

Returns:

  • (Boolean)


97
98
99
# File 'lib/rigor/type/constant.rb', line 97

def ==(other)
  other.is_a?(Constant) && value.class == other.value.class && value == other.value
end

#acceptsAcceptsResult

Parameters:

  • other (Type::t)
  • mode: (accepts_mode)

Returns:



78
# File 'sig/rigor/type.rbs', line 78

def accepts: (Type::t other, ?mode: accepts_mode) -> AcceptsResult

#botTrinary

Returns:



76
# File 'sig/rigor/type.rbs', line 76

def bot: () -> Trinary

#describe(_verbosity = :short) ⇒ String

Date#inspect / DateTime#inspect spell out the internal astronomical-Julian-day representation, which is unreadable in a diagnostic. ISO-8601 is the compact, deterministic form. Time#inspect is already compact (2026-01-01 00:00:00 UTC), so it keeps the default.

Parameters:

  • verbosity (Symbol)

Returns:

  • (String)


68
69
70
71
72
73
# File 'lib/rigor/type/constant.rb', line 68

def describe(_verbosity = :short)
  case value
  when Date then value.iso8601
  else value.inspect
  end
end

#dynamicTrinary

Returns:



77
# File 'sig/rigor/type.rbs', line 77

def dynamic: () -> Trinary

#erase_to_rbsString

RBS supports Literal types for booleans, nil, integer literals (positive and negative), symbol literals, and string literals. Erasing to these preserves the carrier's precision at the RBS boundary — Constant<64> round-trips as 64, not as Integer — and RbsTypeTranslator#translate_literal already maps the parsed RBS Literal back to Constant. Scalar carriers without RBS Literal support (Float, Range, Rational, Complex, Regexp, Pathname) keep their pre-existing widen-to-class-name behaviour because RBS rejects their literal spellings as syntax errors.

Returns:

  • (String)


82
83
84
85
86
87
88
89
90
91
# File 'lib/rigor/type/constant.rb', line 82

def erase_to_rbs
  case value
  when true then "true"
  when false then "false"
  when nil then "nil"
  when Integer then value.to_s
  when Symbol, String then value.inspect
  else value.class.name
  end
end

#freezable_carrier?(value) ⇒ Boolean

Mutable-ish carriers are stored as a frozen copy so a later in-place mutation cannot rewrite the literal under us. Time joins String / Set here: Time#localtime mutates the receiver's zone in place, so the carrier holds a frozen copy (the catalog also blocklists the mutators). Date is already immutable, but is duped-and-frozen for symmetry.

Returns:

  • (Boolean)


60
61
62
63
# File 'lib/rigor/type/constant.rb', line 60

def freezable_carrier?(value)
  value.is_a?(String) || value.is_a?(::Set) ||
    value.is_a?(Date) || value.is_a?(Time)
end

#hashInteger

Returns:

  • (Integer)


102
103
104
# File 'lib/rigor/type/constant.rb', line 102

def hash
  [Constant, value.class, value].hash
end

#inspectString

Returns:

  • (String)


106
107
108
# File 'lib/rigor/type/constant.rb', line 106

def inspect
  "#<Rigor::Type::Constant #{describe(:short)}>"
end

#topTrinary

Returns:



75
# File 'sig/rigor/type.rbs', line 75

def top: () -> Trinary