Bug #19733
closedKernel#Rational does not accept prefix 0
Description
Integer
and Rational
literals accept prefix 0
. There is no difference in this respect.
0b10 # => 2
0b10r # => (2/1)
However, when it comes to Kernel#Integer
and Kernel#Rational
, the former accepts prefix 0
while the latter does not. This is confusing. And as I do not see any reason they should behave differently, I think it is a bug.
Integer("0b10") # => 2
Rational("0b10") # !> ArgumentError
Updated by mame (Yusuke Endoh) 11 months ago
- Status changed from Open to Feedback
"Confusing" is not necessarily a bug. What Kernel#Rational accepts is clearly stated in the documentation in BNF style.
https://docs.ruby-lang.org/en/master/Kernel.html#method-i-Rational
There may be room for discussion to make Kernel#Rational
accept 0x10, though I personally don't need it. An explanation of a use case would help the discussion.
Updated by sawa (Tsuyoshi Sawada) 11 months ago
mame (Yusuke Endoh) wrote in #note-2:
"Confusing" is not necessarily a bug. What Kernel#Rational accepts is clearly stated in the documentation in BNF style.
https://docs.ruby-lang.org/en/master/Kernel.html#method-i-Rational
I had read that documentation, and thought that perhaps someone is going to mention. The BNF in that documentation is broken as it does not mention the fact that the denominator can take a fractional part.
Rational("1/1.2") # => (5/6)
According to the documentation you mention, the expression above should raise an argument error, contrary to fact. So, that documentation is not reliable any way.
Using your words, either the documentation or the fact I mentioned above in this comment is clearly a bug, if the issue I raised is not.
Updated by sawa (Tsuyoshi Sawada) 11 months ago
@mame (Yusuke Endoh) It seems that you were not aware of the fact that the documentation is wrong. And I hope you share with me the idea that we want the documentation to be correct. I think the reason why there was a mistake in the documentation, and why you have not noticed it, is either because the specification is ad hoc or it was not implemented in the way it was intended or in the way people would normally expect. I believe this has to be solved in some way.
I can say that the current behavior is counter-intuitive. Even though Integer
class is not a subclass of Rational
class, coercion relation indicates that whatever can be interpreted as an Integer
can be interpreted as a Rational
but not vice versa. And I think people would normally expect whatever is interpretable by Kernel#Integer
is interpretable by Kernel#Rational
.
A use case that I can think of where this becomes relevant is an arithmetic program in which some string (from user input or from some data) is attempted to be interpreted using methods like Kernel#Rational
or Kernel#Integer
one after another (avoiding the use of methods like eval
in fear of malicious code). Such program may attempt interpretation of a string by Kernel#Rational
, and if it succeeds, go on to attempt interpretation by Kernel#Integer
to see if the interpretation can be further narrowed down. However, if a non-decimal integer string is passed to Kernel#Rational
and an error is raised, such program would give up attempting interpretation by Kernel#Integer
, even though it would succeed, and that input string would be thrown away.
Even more strange is the fact that, in case of hexadecimal 0x
(but not with other 0
prefixes), it becomes interpretable again by Kernel#Float
. This is a real mess.
Integer("0x10") # => 16
Rational("0x10") # !> ArgumentError
Float("0x10") # => 16.0