Class: Rigor::Type::IntegerRange

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/type/integer_range.rb

Overview

A bounded integer range carrier. Each bound is either an ‘Integer` or one of the symbolic infinities `:neg_infinity` / `:pos_infinity`. Inspired by PHPStan’s ‘int<min, max>` family — the named aliases `positive-int` (1..), `non-negative-int` (0..), `negative-int` (..-1), `non-positive-int` (..0) all surface through this single carrier and are recovered in `describe` for human-friendly output.

Constraints on construction:

  • both bounds must be either ‘Integer` or one of the two infinity sentinels;

  • if both bounds are concrete, ‘min <= max` must hold;

  • the universal case ‘(-∞, +∞)` is structurally distinct from `Nominal` — it carries no extra information today but keeps the carrier closed under range narrowing.

Erasure to RBS is always “Integer”: RBS itself does not natively express bounded integer ranges.

Constant Summary collapse

NEG_INFINITY =
:neg_infinity
POS_INFINITY =
:pos_infinity
INFINITIES =
[NEG_INFINITY, POS_INFINITY].freeze
ALIAS_NAMES =
{
  [NEG_INFINITY, POS_INFINITY] => "int",
  [1, POS_INFINITY] => "positive-int",
  [0, POS_INFINITY] => "non-negative-int",
  [NEG_INFINITY, -1] => "negative-int",
  [NEG_INFINITY, 0] => "non-positive-int"
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(min, max) ⇒ IntegerRange

Returns a new instance of IntegerRange.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/rigor/type/integer_range.rb', line 31

def initialize(min, max)
  validate_bound!(min, "min")
  validate_bound!(max, "max")
  if min.is_a?(Integer) && max.is_a?(Integer) && min > max
    raise ArgumentError, "IntegerRange requires min (#{min}) <= max (#{max})"
  end
  if min == POS_INFINITY || max == NEG_INFINITY
    raise ArgumentError, "IntegerRange bounds out of order: min=#{min.inspect}, max=#{max.inspect}"
  end

  @min = min
  @max = max
  freeze
end

Instance Attribute Details

#maxObject (readonly)

Returns the value of attribute max.



29
30
31
# File 'lib/rigor/type/integer_range.rb', line 29

def max
  @max
end

#minObject (readonly)

Returns the value of attribute min.



29
30
31
# File 'lib/rigor/type/integer_range.rb', line 29

def min
  @min
end

Instance Method Details

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



114
115
116
# File 'lib/rigor/type/integer_range.rb', line 114

def ==(other)
  other.is_a?(IntegerRange) && min == other.min && max == other.max
end

#accepts(other, mode: :gradual) ⇒ Object



110
111
112
# File 'lib/rigor/type/integer_range.rb', line 110

def accepts(other, mode: :gradual)
  Inference::Acceptance.accepts(self, other, mode: mode)
end

#botObject



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

def bot
  Trinary.no
end

#cardinalityObject



54
55
56
# File 'lib/rigor/type/integer_range.rb', line 54

def cardinality
  finite? ? (max - min + 1) : Float::INFINITY
end

#covers?(int) ⇒ Boolean

Returns:

  • (Boolean)


58
59
60
61
62
# File 'lib/rigor/type/integer_range.rb', line 58

def covers?(int)
  return false unless int.is_a?(Integer)

  int.between?(lower, upper)
end

#describe(_verbosity = :short) ⇒ Object



83
84
85
# File 'lib/rigor/type/integer_range.rb', line 83

def describe(_verbosity = :short)
  ALIAS_NAMES[[min, max]] || generic_description
end

#dynamicObject



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

def dynamic
  Trinary.no
end

#erase_to_rbsObject



94
95
96
# File 'lib/rigor/type/integer_range.rb', line 94

def erase_to_rbs
  "Integer"
end

#finite?Boolean

Returns:

  • (Boolean)


50
51
52
# File 'lib/rigor/type/integer_range.rb', line 50

def finite?
  min.is_a?(Integer) && max.is_a?(Integer)
end

#generic_descriptionObject



87
88
89
90
91
92
# File 'lib/rigor/type/integer_range.rb', line 87

def generic_description
  return "int<#{min}, max>" if max == POS_INFINITY
  return "int<min, #{max}>" if min == NEG_INFINITY

  "int<#{min}, #{max}>"
end

#hashObject



119
120
121
# File 'lib/rigor/type/integer_range.rb', line 119

def hash
  [IntegerRange, min, max].hash
end

#inspectObject



123
124
125
# File 'lib/rigor/type/integer_range.rb', line 123

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

#lowerObject

Returns the lower bound as a numeric (with ‘-Float::INFINITY` for `:neg_infinity`). Use this in arithmetic comparisons; never compare `:neg_infinity` directly with an `Integer`.



67
68
69
# File 'lib/rigor/type/integer_range.rb', line 67

def lower
  min == NEG_INFINITY ? -Float::INFINITY : min
end

#topObject



98
99
100
# File 'lib/rigor/type/integer_range.rb', line 98

def top
  Trinary.no
end

#universal?Boolean

Returns:

  • (Boolean)


46
47
48
# File 'lib/rigor/type/integer_range.rb', line 46

def universal?
  min == NEG_INFINITY && max == POS_INFINITY
end

#upperObject



71
72
73
# File 'lib/rigor/type/integer_range.rb', line 71

def upper
  max == POS_INFINITY ? Float::INFINITY : max
end