https://redmine.ruby-lang.org/https://redmine.ruby-lang.org/favicon.ico?17113305112016-09-07T06:55:29ZRuby Issue Tracking SystemRuby master - Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exceptionhttps://redmine.ruby-lang.org/issues/12732?journal_id=603942016-09-07T06:55:29Ztenderlovemaking (Aaron Patterson)tenderlove@ruby-lang.org
<ul><li><strong>File</strong> <a href="/attachments/6133">integer-parse.pdf</a> <a class="icon-only icon-download" title="Download" href="/attachments/download/6133/integer-parse.pdf">integer-parse.pdf</a> added</li></ul><p>Adding a slide to show code I'm actually writing vs want to write</p> Ruby master - Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exceptionhttps://redmine.ruby-lang.org/issues/12732?journal_id=608472016-10-11T11:16:24Zshyouhei (Shyouhei Urabe)shyouhei@ruby-lang.org
<ul></ul><p>We looked at this issue in developer meeting today.</p>
<p>It seems originally, ruby was designed under assumption that string to integer conversion in general could be covered 100% by either to_s or Integer(). Truth is we need the proposed functionality.</p>
<p>People at the meeting was not sure about the API though. Is it a variant of Integer() or a separate new method? For instance an attendee suggested "Integer?()" but could not be popular.</p> Ruby master - Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exceptionhttps://redmine.ruby-lang.org/issues/12732?journal_id=616132016-11-22T10:08:55Zshyouhei (Shyouhei Urabe)shyouhei@ruby-lang.org
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-1 priority-4 priority-default" href="/issues/12968">Feature #12968</a>: Allow default value via block for Integer(), Float() and Rational()</i> added</li></ul> Ruby master - Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exceptionhttps://redmine.ruby-lang.org/issues/12732?journal_id=616702016-11-25T07:01:23Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Feedback</i></li><li><strong>Assignee</strong> set to <i>matz (Yukihiro Matsumoto)</i></li></ul><p>Is there any problem with the following code?</p>
<pre><code>Integer(str) rescue default_value
</code></pre>
<p>Matz.</p> Ruby master - Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exceptionhttps://redmine.ruby-lang.org/issues/12732?journal_id=617132016-11-25T09:34:57Znaruse (Yui NARUSE)naruse@airemix.jp
<ul></ul><p>Below is PoC; it may have a path which raises an exception.</p>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gh">diff --git a/object.c b/object.c
index 05bef4d..5d63803 100644
</span><span class="gd">--- a/object.c
</span><span class="gi">+++ b/object.c
</span><span class="p">@@ -2750,17 +2750,60 @@</span> static VALUE
rb_f_integer(int argc, VALUE *argv, VALUE obj)
{
VALUE arg = Qnil;
<span class="gi">+ VALUE opts = Qnil;
+ VALUE exception = Qnil;
+ VALUE vbase = Qundef;
</span> int base = 0;
<span class="gi">+ static ID int_kwds[1];
</span>
<span class="gd">- switch (argc) {
- case 2:
- base = NUM2INT(argv[1]);
- case 1:
- arg = argv[0];
- break;
- default:
- /* should cause ArgumentError */
- rb_scan_args(argc, argv, "11", NULL, NULL);
</span><span class="gi">+ rb_scan_args(argc, argv, "11:", &arg, &vbase, &opts);
+ if (!NIL_P(vbase)) {
+ base = NUM2INT(vbase);
+ }
+ if (!NIL_P(opts)) {
+ if (!int_kwds[0]) {
+ int_kwds[0] = rb_intern_const("exception");
+ }
+ if (rb_get_kwargs(opts, int_kwds, 0, 1, &exception)) {
+ VALUE tmp;
+ if (RB_FLOAT_TYPE_P(arg)) {
+ double f;
+ if (base != 0) goto arg_error;
+ f = RFLOAT_VALUE(arg);
+ if (FIXABLE(f)) return LONG2FIX((long)f);
+ return rb_dbl2big(f);
+ }
+ else if (RB_INTEGER_TYPE_P(arg)) {
+ if (base != 0) goto arg_error;
+ return arg;
+ }
+ else if (RB_TYPE_P(arg, T_STRING)) {
+ const char *s;
+ long len;
+ rb_must_asciicompat(arg);
+ RSTRING_GETMEM(arg, s, len);
+ tmp = rb_cstr_parse_inum(s, len, NULL, base);
+ if (NIL_P(tmp)) {
+ return exception;
+ }
+ return tmp;
+ }
+ else if (NIL_P(arg)) {
+ if (base != 0) goto arg_error;
+ return exception;
+ }
+ if (base != 0) {
+ tmp = rb_check_string_type(arg);
+ if (!NIL_P(tmp)) return rb_str_to_inum(tmp, base, TRUE);
+arg_error:
+ rb_raise(rb_eArgError, "base specified for non string value");
+ }
+ tmp = convert_type(arg, "Integer", "to_int", FALSE);
+ if (NIL_P(tmp)) {
+ return rb_to_integer(arg, "to_i");
+ }
+ return tmp;
+ }
</span> }
return rb_convert_to_integer(arg, base);
}
</code></pre>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">assert</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
<span class="k">if</span> <span class="n">a</span> <span class="o">!=</span> <span class="n">b</span>
<span class="k">raise</span> <span class="s2">"'</span><span class="si">#{</span><span class="n">a</span><span class="si">}</span><span class="s2">' != '</span><span class="si">#{</span><span class="n">b</span><span class="si">}</span><span class="s2">'"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">assert_raise</span><span class="p">(</span><span class="n">ex</span><span class="p">)</span>
<span class="k">begin</span>
<span class="k">yield</span>
<span class="k">raise</span> <span class="s2">"</span><span class="si">#{</span><span class="n">ex</span><span class="si">}</span><span class="s2"> is expected but not raised"</span>
<span class="k">rescue</span> <span class="n">ex</span>
<span class="c1"># correct</span>
<span class="k">rescue</span>
<span class="k">raise</span> <span class="s2">"</span><span class="si">#{</span><span class="n">ex</span><span class="si">}</span><span class="s2"> is expected but </span><span class="si">#{</span><span class="vg">$!</span><span class="p">.</span><span class="nf">inspect</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">o</span> <span class="o">=</span> <span class="no">Object</span><span class="p">.</span><span class="nf">new</span>
<span class="n">assert</span> <span class="mi">123</span><span class="p">,</span> <span class="no">Integer</span><span class="p">(</span><span class="s2">"123"</span><span class="p">)</span>
<span class="n">assert</span> <span class="mi">50</span><span class="p">,</span> <span class="no">Integer</span><span class="p">(</span><span class="s2">"32"</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
<span class="n">assert</span> <span class="mi">16</span><span class="p">,</span> <span class="no">Integer</span><span class="p">(</span><span class="s2">"10"</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="ss">exception: </span><span class="n">o</span><span class="p">)</span>
<span class="n">assert</span> <span class="n">o</span><span class="p">,</span> <span class="no">Integer</span><span class="p">(</span><span class="s2">"x"</span><span class="p">,</span> <span class="ss">exception: </span><span class="n">o</span><span class="p">)</span>
<span class="n">assert</span> <span class="n">o</span><span class="p">,</span> <span class="no">Integer</span><span class="p">(</span><span class="s2">"x"</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="ss">exception: </span><span class="n">o</span><span class="p">)</span>
<span class="n">assert_raise</span><span class="p">(</span><span class="no">ArgumentError</span><span class="p">){</span> <span class="no">Integer</span><span class="p">(</span><span class="s2">"x"</span><span class="p">)</span> <span class="p">}</span>
<span class="n">assert_raise</span><span class="p">(</span><span class="no">ArgumentError</span><span class="p">){</span> <span class="no">Integer</span><span class="p">(</span><span class="s2">"x"</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> <span class="p">}</span>
</code></pre>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">require</span><span class="s1">'benchmark/ips'</span>
<span class="no">Benchmark</span><span class="p">.</span><span class="nf">ips</span><span class="p">{</span><span class="o">|</span><span class="n">x</span><span class="o">|</span>
<span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"rescue"</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Integer</span><span class="p">(</span><span class="s1">'foo'</span><span class="p">)</span> <span class="k">rescue</span> <span class="kp">nil</span>
<span class="p">}</span>
<span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"kwarg"</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Integer</span><span class="p">(</span><span class="s1">'foo'</span><span class="p">,</span> <span class="ss">exception: </span><span class="kp">nil</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre>
<pre><code>Warming up --------------------------------------
rescue 36.258k i/100ms
kwarg 64.004k i/100ms
Calculating -------------------------------------
rescue 392.926k (± 8.9%) i/s - 1.958M in 5.025204s
kwarg 844.563k (±14.9%) i/s - 4.096M in 5.017539s
</code></pre> Ruby master - Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exceptionhttps://redmine.ruby-lang.org/issues/12732?journal_id=617782016-11-28T23:01:39Ztenderlovemaking (Aaron Patterson)tenderlove@ruby-lang.org
<ul></ul><p>Hi,</p>
<blockquote>
<p>Is there any problem with the following code?<br>
Integer(str) rescue default_value</p>
</blockquote>
<p>2 problems</p>
<ol>
<li>It's slower than it could be (as Naruse demonstrates)</li>
<li>It's very noisy when <code>-d</code> is enabled.</li>
</ol>
<p>In Psych, I am trying to avoid noise from <code>-d</code>. That means I have to try to check if the string will work with <code>Integer()</code>, then actually call <code>Integer()</code>. It means the string has to be parsed twice. If <code>Integer(str) rescue default_value</code> didn't make noise, then I would be OK with that. :)</p> Ruby master - Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exceptionhttps://redmine.ruby-lang.org/issues/12732?journal_id=628972017-02-07T11:56:43Zrbjl (Jan Lelis)hi@ruby.consulting
<ul></ul><p>Although it does not solve Aaron's use case, I would suggest to have a <code>Integer.try_convert</code>, <code>Float.try_convert</code>, <code>Rational.try_convert</code>, and <code>Complex.try_convert</code> which do not raise exceptions, but just return <code>nil</code>. To keep consistency, they would just call implicit, then explicit conversion (e.g. <code>to_int</code>, then <code>to_i</code>), instead of <code>Integer()</code>'s special parsing.</p>
<p>It only allows strict parsing, but is much cleaner, imho. Also, it would fill some empty spots in the <a href="http://idiosyncratic-ruby.com/54-try-converting.html#core-classes-conversion-table" class="external">core conversion table</a> and make Ruby's conversion logic simpler. (<code>.try_convert</code>, which currently feels more like an implementation detail, could then be embraced more).</p>
<p>To allow <code>Integer()</code> special conversion, it would still need an <code>exception:</code> option, but also <code>Float()</code>, <code>Rational()</code>, and <code>Complex()</code> would need it (since they currently also lack this feature due to not having a <code>try_convert</code>). One idea is to give every of the uppercased Kernel methods an <code>exception:</code> option, but this does not make sense for <code>Array()</code> and just would not be needed if going for the broader <code>try_convert</code> support).</p>
<p>To summarize my suggestion in two bullet points:</p>
<ul>
<li>Create <code>Integer.try_convert</code>, <code>Float.try_convert</code>, <code>Rational.try_convert</code>, and <code>Complex.try_convert</code> which prefer implicit conversion (if available), then explicit conversion, but return <code>nil</code> instead of raising an exception</li>
<li>Give <code>Integer()</code> an <code>exception</code> option to support special integer parsing without exceptions, but do not give <code>Float()</code> one</li>
</ul> Ruby master - Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exceptionhttps://redmine.ruby-lang.org/issues/12732?journal_id=697422018-01-24T07:54:56Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p>Aaron's comment in <a href="#note-6">#note-6</a> sounds reasonable. Accepted.</p>
<p>Matz.</p> Ruby master - Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exceptionhttps://redmine.ruby-lang.org/issues/12732?journal_id=697432018-01-24T08:00:16Zknu (Akinori MUSHA)knu@ruby-lang.org
<ul></ul><p>Just for the record, <code>Integer(x, rescue: default_value)</code> might be an idea, if anything other than <code>nil</code> (like zero) would be desired.</p> Ruby master - Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exceptionhttps://redmine.ruby-lang.org/issues/12732?journal_id=702562018-02-07T19:17:49Ztenderlovemaking (Aaron Patterson)tenderlove@ruby-lang.org
<ul></ul><p>Something like <code>Integer(x, rescue: default_value)</code> is fine for me too, (or <code>Integer(x, ->() { default_value })</code>, which is similar to <code>[].find(->() { missing_value }) { ... }</code>) Configuring with a default value seems more flexible.</p> Ruby master - Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exceptionhttps://redmine.ruby-lang.org/issues/12732?journal_id=702742018-02-08T12:49:39Znobu (Nobuyoshi Nakada)nobu@ruby-lang.org
<ul></ul><p>Since <code>Integer()</code> has <code>radix</code> optional argument, new optional argument might be confusing.<br>
A keyword argument or a block would be better, I think.</p> Ruby master - Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exceptionhttps://redmine.ruby-lang.org/issues/12732?journal_id=702812018-02-08T17:47:22Zenebo (Thomas Enebo)tom.enebo@gmail.com
<ul></ul><p>Two comments:</p>
<ol>
<li>having block form only defeats any performance gain as executing blocks have a measurable cost. It may be nice to have though in addition to simple nil return form.</li>
<li>it would be really nice if Ruby had some API consistency for non-exception variants of the various callswhich have made this change. Doing each one of these as a one-off discussion almost destines these APIs to not be consistent.</li>
</ol> Ruby master - Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exceptionhttps://redmine.ruby-lang.org/issues/12732?journal_id=710032018-03-15T07:19:46Zmrkn (Kenta Murata)muraken@gmail.com
<ul><li><strong>Status</strong> changed from <i>Feedback</i> to <i>Closed</i></li></ul><p>Applied in changeset ruby-trunk:trunk|r62757.</p>
<hr>
<p>Add <code>exception:</code> keyword in Kernel#Integer()</p>
<p>Support <code>exception:</code> keyword argument in Kernel#Integer().<br>
If <code>exception:</code> is <code>false</code>, <code>Kernel#Integer()</code> returns <code>nil</code> if the given<br>
value cannot be interpreted as an integer value.<br>
The default value of <code>exception:</code> is <code>true</code>.<br>
This is part of [Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exception (Closed)" href="https://redmine.ruby-lang.org/issues/12732">#12732</a>].</p> Ruby master - Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exceptionhttps://redmine.ruby-lang.org/issues/12732?journal_id=722932018-05-29T11:35:46Zm_s__santos (Matheus Silva)
<ul></ul><p>rbjl (Jan Lelis) wrote:</p>
<blockquote>
<p>Although it does not solve Aaron's use case, I would suggest to have a <code>Integer.try_convert</code>, <code>Float.try_convert</code>, <code>Rational.try_convert</code>, and <code>Complex.try_convert</code> which do not raise exceptions, but just return <code>nil</code>. To keep consistency, they would just call implicit, then explicit conversion (e.g. <code>to_int</code>, then <code>to_i</code>), instead of <code>Integer()</code>'s special parsing.</p>
<p>It only allows strict parsing, but is much cleaner, imho. Also, it would fill some empty spots in the <a href="http://idiosyncratic-ruby.com/54-try-converting.html#core-classes-conversion-table" class="external">core conversion table</a> and make Ruby's conversion logic simpler. (<code>.try_convert</code>, which currently feels more like an implementation detail, could then be embraced more).</p>
<p>To allow <code>Integer()</code> special conversion, it would still need an <code>exception:</code> option, but also <code>Float()</code>, <code>Rational()</code>, and <code>Complex()</code> would need it (since they currently also lack this feature due to not having a <code>try_convert</code>). One idea is to give every of the uppercased Kernel methods an <code>exception:</code> option, but this does not make sense for <code>Array()</code> and just would not be needed if going for the broader <code>try_convert</code> support).</p>
<p>To summarize my suggestion in two bullet points:</p>
<ul>
<li>Create <code>Integer.try_convert</code>, <code>Float.try_convert</code>, <code>Rational.try_convert</code>, and <code>Complex.try_convert</code> which prefer implicit conversion (if available), then explicit conversion, but return <code>nil</code> instead of raising an exception</li>
<li>Give <code>Integer()</code> an <code>exception</code> option to support special integer parsing without exceptions, but do not give <code>Float()</code> one</li>
</ul>
</blockquote>
<p>It would be better if <code>Integer.try_convert</code> return the conversion or the value passed if it can't convert.</p> Ruby master - Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exceptionhttps://redmine.ruby-lang.org/issues/12732?journal_id=955562021-12-23T23:41:09Zhsbt (Hiroshi SHIBATA)hsbt@ruby-lang.org
<ul><li><strong>Project</strong> changed from <i>14</i> to <i>Ruby master</i></li></ul>