rbs icon indicating copy to clipboard operation
rbs copied to clipboard

The `range[T]` type definition is to restrictive

Open sampersand opened this issue 2 years ago • 0 comments

The current definition, type range[T] = Range[T] | _Range[T] is mostly correct, except it implicitly requires T to implement <=>: Range cannot be constructed unless T defines <=>. This imposes the <=> limitation on _Range[T], which doesn't actually require it:

MyInt = Struct.new(:to_int)
MyCmpInt = Struct.new(:to_int) do
	def <=>(rhs) = to_int <=> rhs.to_int
end

MyRange = Struct.new(:begin, :end, :exclude_end) do
	alias exclude_end? exclude_end
end

rng1 = MyInt.new(1)..MyInt.new(2)       #=> bad value for range (ArgumentError)
rng2 = MyCmpInt.new(1)..MyCmpInt.new(2) #=> ok!

myrng1 = MyRange.new(MyInt.new(1), MyInt.new(2))       #=> ok!
myrng2 = MyRange.new(MyCmpInt.new(1), MyCmpInt.new(2)) #=> ok!

This is particularly relevant for functions which accept range[int] (or range[int?]), such as String#[]:

puts "hello world"[rng2]   #=> el
puts "hello world"[myrng1] #=> el
puts "hello world"[myrng2] #=> el

The current definition would preclude myrng1 from being a valid argument, as MyInt doesn't define <=>. The solution, I think, is to change range[T]'s definition:

type range[T] = Range[T & Comparable::_WithSpaceshipOperator] | _Range[T]

sampersand avatar Sep 03 '23 23:09 sampersand