Feature #20235
openDeprecate CHAR syntax
Description
I propose deprecating the ?c
syntax. It served a purpose in ruby <= 1.8, but no longer.
The reason I'm proposing this is because today I ran into this error:
p $stdin.closed?=>true # comparison of String with true failed (ArgumentError)
I was completed mystified, and had to resort to Ripper to figure out what's going on
p *Ripper.lex("p $stdin.closed?=>true")
[[1, 0], :on_ident, "p", CMDARG]
[[1, 1], :on_sp, " ", CMDARG]
[[1, 2], :on_gvar, "$stdin", END]
[[1, 8], :on_period, ".", DOT]
[[1, 9], :on_ident, "closed", ARG]
[[1, 15], :on_CHAR, "?=", END] #OOOOHH!!!!!
[[1, 17], :on_op, ">", BEG]
[[1, 18], :on_kw, "true", END]
We don't have to commit to a removal schedule right now, but I think it would at least be good to print a deprecation message if $VERBOSE.
Updated by rubyFeedback (robert heiler) 11 months ago
I think the first time - and probably the only time - I heard
about the ?a syntax:
?a # => "a"
?b # => "b"
?c # => "c"
was in the pickaxe book. I believe there was a chapter in the
bookaxe that mentioned the ? syntax along with .chr and .ord,
or something like that.
I don't recall ever having used that syntax again in like 15
years, so from that point of view I agree with Dan0042's analysis.
Then again I don't know all possible use cases, but provided
if there are no substantial use cases for that syntax, then
Dan0042 may have a point.
p $stdin.closed?=>true
leads to an error, whereas
p $stdin.closed? =>true
does not lead to an error. Although perhaps the parser could
differentiate between these two use cases; but the basic question
is still how useful ?a and how popular it may still be.
Perhaps there is some way to find out how often ruby users use
the ?char syntax? I can not recall any gem using it actually.
Updated by shan (Shannon Skipper) 11 months ago
A $stdin.closed?#=>true
would work as expected. You can't generally follow Ruby code immediately with an uncommented =>
designating a return value and have it not break.
It made sense to maintain character literals for compatibility between Ruby 1.8 and 1.9, since String#[] and the character literal both returned an ordinal. So with string = 'a'
a string[0]
would be 97
like an ?a
literal. In the time where Ruby 1.8 and Ruby 1.9 were both a thing, using character literals was strongly discouraged in Ruby 1.9 due to a likely incompatibility with 1.8.
Now that 1.8 is long gone, a character literal is now once again perfectly safe. What I question is whether we should resume using character literals, since they're now fine to use, or whether their continued existence was purely for 1.8 compat and they should be deprecated and retired. It would be really nice to document which is the case.
Updated by Dan0042 (Daniel DeLorme) 11 months ago
@shan (Shannon Skipper) it's not "an uncommented =>
designating a return value", it's (supposed to be) a hash literal like p 1=>2
. With spaces maybe it's more obvious: p $stdin.closed? => true
In ruby 1.8 character literals were useful for things like "hello"[1] == ?e
. But nowadays there's not a single reason to use ?e
instead of "e"
.
Updated by shan (Shannon Skipper) 11 months ago
Dan0042 (Daniel DeLorme) wrote in #note-3:
@shan (Shannon Skipper) it's not "an uncommented
=>
designating a return value", it's (supposed to be) a hash literal likep 1=>2
. With spaces maybe it's more obvious:p $stdin.closed? => true
Ah, I misunderstood the intention but should have caught on with the second "does not lead to an error."
In ruby 1.8 character literals were useful for things like
"hello"[1] == ?e
. But nowadays there's not a single reason to use?e
instead of"e"
.
Yeah, agree on their use in 1.8 and why they were carried forward in 1.9 for compatibility. On the other hand, a ?e
or ?\n
can look crisp compared to a 'e'
or "\n"
, make it clear to the reader on quick glance that it's a single character. Since some folk still use them for stylistic reasons there would be some compatibility issues removing them.
If they were only kept for 1.8 compatibility it might be nice to document that and deprecate them now that 1.8 compatibility is a moot point. On the other hand, TIMTOWTDI and with no worry about Ruby 1.8 anymore so they could just be embraced as a safe and predictable alternative to String literals for single characters. I'm curious if Matz intends them to continue to be used and be a future part of the language or would discourage all modern usage?
Updated by nobu (Nobuyoshi Nakada) 11 months ago
Maybe want to parse closed?=>true
as (closed?)=>true
?
If only it is, ?c
syntax doesn't feel to need to be removed.
Updated by mame (Yusuke Endoh) 11 months ago
Currently, !
and ?
are interpreted as a part of the method name unless they are followed by =
. This trick is needed to interpret foo!=bar
as foo != bar
, not a method name foo!
. I think this trick is not needed very much for ?
, so we may relax the condition to allow ?
followed by =
as a method name. The following patch will solve the original issue.
diff --git a/parse.y b/parse.y
index 6836576d80..4db724a289 100644
--- a/parse.y
+++ b/parse.y
@@ -10657,7 +10657,7 @@ parse_ident(struct parser_params *p, int c, int cmd_state)
if (tokadd_mbchar(p, c) == -1) return 0;
c = nextc(p);
} while (parser_is_identchar(p));
- if ((c == '!' || c == '?') && !peek(p, '=')) {
+ if ((c == '!' && !peek(p, '=')) || c == '?') {
result = tFID;
tokadd(p, c);
}
However, note that the following code would still violate the OP's expectation.
p $stdin.raw!=>true
Updated by Dan0042 (Daniel DeLorme) 11 months ago
I agree, it would be good to fix the parsing. At least that's a bugfix that can be done now.
In the long term I think warning about char literals would also be a good thing. Because I stumbled on a problem with p $stdin.closed?=>true
its make me a bit paranoid about what other problems might be there because of char literals, hidden?
Regarding p $stdin.raw!=>true
that's not related to char literals but I think it should be fixed also. Maybe:
diff --git a/parse.y b/parse.y
--- a/parse.y
+++ b/parse.y
@@ -10657,7 +10657,7 @@ parse_ident(struct parser_params *p, int c, int cmd_state)
if (tokadd_mbchar(p, c) == -1) return 0;
c = nextc(p);
} while (parser_is_identchar(p));
- if ((c == '!' || c == '?') && !peek(p, '=')) {
+ if (c == '?' || (c == '!' && (!peek(p, '=') || peek_n(p, '>', 1) || peek_n(p, '=', 1) || peek_n(p, '~', 1)))) {
result = tFID;
tokadd(p, c);
}