https://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112015-11-07T21:17:51ZRuby Issue Tracking SystemRuby master - Feature #11665: Support nested functions for better code organizationhttps://bugs.ruby-lang.org/issues/11665?journal_id=547542015-11-07T21:17:51Zkeithrbennett (Keith Bennett)keithrbennett@gmail.com
<ul><li><strong>Tracker</strong> changed from <i>Bug</i> to <i>Feature</i></li></ul> Ruby master - Feature #11665: Support nested functions for better code organizationhttps://bugs.ruby-lang.org/issues/11665?journal_id=547642015-11-09T06:43:21ZHanmac (Hans Mackowiak)hanmac@gmx.de
<ul></ul><p>currently its possible to define methods inside of methods:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">A</span>
<span class="k">def</span> <span class="nf">start</span>
<span class="k">def</span> <span class="nf">method1</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">method2</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">a</span> <span class="o">=</span> <span class="no">A</span><span class="p">.</span><span class="nf">new</span>
<span class="n">a</span><span class="p">.</span><span class="nf">methods</span> <span class="c1">#=> [start]</span>
<span class="n">a</span><span class="p">.</span><span class="nf">start</span>
<span class="n">a</span><span class="p">.</span><span class="nf">methods</span> <span class="c1">#=> [start, method1, method2]</span>
</code></pre>
<p>but i think that might not what you want</p> Ruby master - Feature #11665: Support nested functions for better code organizationhttps://bugs.ruby-lang.org/issues/11665?journal_id=547692015-11-09T07:37:00Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p>I am not sure nested functions are what we need. Maybe we need "real functions" with Java|C++ private scope.<br>
Besides that, semantics for nested function is totally new in Ruby language. It cannot be a method call nor lambda.<br>
So I don't think we can have this feature or something similar in Ruby for the near future.</p>
<p>But at least, the current behavior of nested method definition is useless. It should be made obsolete to open up the future possibility (I'd vote for warning).</p>
<p>Matz.</p> Ruby master - Feature #11665: Support nested functions for better code organizationhttps://bugs.ruby-lang.org/issues/11665?journal_id=547702015-11-09T07:37:44Zko1 (Koichi Sasada)
<ul></ul><p>Discussion: <a href="https://docs.google.com/document/d/1D0Eo5N7NE_unIySOKG9lVj_eyXf66BQPM4PKp7NvMyQ/pub" class="external">https://docs.google.com/document/d/1D0Eo5N7NE_unIySOKG9lVj_eyXf66BQPM4PKp7NvMyQ/pub</a></p>
<p>Feel free to continue discussion on this ticket.</p> Ruby master - Feature #11665: Support nested functions for better code organizationhttps://bugs.ruby-lang.org/issues/11665?journal_id=547722015-11-09T07:40:56Zko1 (Koichi Sasada)
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-1 priority-4 priority-default" href="/issues/11670">Feature #11670</a>: Show warning to make nested def obsolete </i> added</li></ul> Ruby master - Feature #11665: Support nested functions for better code organizationhttps://bugs.ruby-lang.org/issues/11665?journal_id=547832015-11-09T12:06:52Znobu (Nobuyoshi Nakada)nobu@ruby-lang.org
<ul></ul><p>private or scope local method.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">X</span>
<span class="n">using</span> <span class="no">Module</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span>
<span class="n">refine</span> <span class="no">X</span> <span class="k">do</span>
<span class="k">def</span> <span class="nf">a</span>
<span class="ss">:a</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="p">}</span>
<span class="k">def</span> <span class="nf">x</span><span class="p">;</span><span class="n">a</span><span class="p">;</span> <span class="k">end</span>
<span class="k">end</span>
<span class="n">x</span> <span class="o">=</span> <span class="no">X</span><span class="p">.</span><span class="nf">new</span>
<span class="nb">p</span> <span class="n">x</span><span class="p">.</span><span class="nf">x</span> <span class="c1">#=> :a</span>
<span class="nb">p</span> <span class="n">x</span><span class="p">.</span><span class="nf">__send__</span><span class="p">(</span><span class="ss">:a</span><span class="p">)</span> <span class="c1">#=> NoMethodError</span>
</code></pre> Ruby master - Feature #11665: Support nested functions for better code organizationhttps://bugs.ruby-lang.org/issues/11665?journal_id=547842015-11-09T12:24:49Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul></ul><blockquote>
<p>But at least, the current behavior of nested method definition is useless</p>
</blockquote>
<p>I'd like to show some cases where the current behavior is actually useful. I'm not against the change, though.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">warn_foo</span>
<span class="nb">warn</span> <span class="s2">"foo"</span>
<span class="k">def</span> <span class="nf">warn_foo</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="mi">3</span><span class="p">.</span><span class="nf">times</span> <span class="k">do</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span>
<span class="nb">p</span> <span class="n">i</span>
<span class="n">warn_foo</span> <span class="c1">#=> warn only once</span>
<span class="k">end</span>
</code></pre>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">enable_log</span>
<span class="k">def</span> <span class="nf">log</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="nb">puts</span> <span class="n">s</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">disable_log</span>
<span class="k">def</span> <span class="nf">log</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">enable_log</span>
<span class="n">log</span><span class="p">(</span><span class="s2">"foo"</span><span class="p">)</span>
<span class="n">disable_log</span>
<span class="n">log</span><span class="p">(</span><span class="s2">"bar"</span><span class="p">)</span>
<span class="n">enable_log</span>
<span class="n">log</span><span class="p">(</span><span class="s2">"baz"</span><span class="p">)</span>
</code></pre>
<p>--<br>
Yusuke Endoh <a href="mailto:mame@ruby-lang.org" class="email">mame@ruby-lang.org</a></p> Ruby master - Feature #11665: Support nested functions for better code organizationhttps://bugs.ruby-lang.org/issues/11665?journal_id=547982015-11-10T06:42:57ZHanmac (Hans Mackowiak)hanmac@gmx.de
<ul></ul><p>apropos Procs and lambda, can't we create one which does not have a binding/access to local variables on the outside?</p>
<p>such a construct might be even faster to run, and maybe even serialize-able</p> Ruby master - Feature #11665: Support nested functions for better code organizationhttps://bugs.ruby-lang.org/issues/11665?journal_id=548212015-11-11T13:21:37Zdanielpclark (Daniel P. Clark)6ftdan@gmail.com
<ul></ul><p>Oddly there are some people who've found the dynamic defining of methods this way useful as a state machine. This recent blog post demonstrates it <a href="http://weblog.jamisbuck.org/2015/10/17/dynamic-def.html" class="external">http://weblog.jamisbuck.org/2015/10/17/dynamic-def.html</a></p>
<p>I am inclined to agree with you Keith about the added compounding of complexity and the potential side effects of lambdas. After thinking about this it sound like what you're looking for is a lot like refinements brought down from class level into method definitions. Nobuyoshi has written an excellent example with <a class="issue tracker-1 status-5 priority-4 priority-default closed behind-schedule" title="Bug: sprintf() of %f on Windows(MSVCRT) (Closed)" href="https://bugs.ruby-lang.org/issues/6">#6</a> . If that could be implemented in a meta-programming way as a method on Class, maybe as <code>scoped_def :m, *a, &b</code> you can use it anywhere.</p>
<p>Here's an example without using refinements</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">A</span>
<span class="k">def</span> <span class="nf">example</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="k">class</span> <span class="o"><<</span> <span class="nb">self</span>
<span class="kp">private</span> <span class="k">def</span> <span class="nf">hello</span>
<span class="s2">"hello"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">x</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
<span class="k">ensure</span> <span class="c1"># if proc call above fails we don't want to leak the method</span>
<span class="k">class</span> <span class="o"><<</span> <span class="nb">self</span>
<span class="k">undef</span> <span class="ss">:hello</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">a</span> <span class="o">=</span> <span class="no">A</span><span class="p">.</span><span class="nf">new</span>
<span class="n">a</span><span class="p">.</span><span class="nf">example</span> <span class="o">-></span><span class="n">i</span><span class="p">{</span> <span class="nb">puts</span> <span class="n">i</span><span class="p">.</span><span class="nf">send</span> <span class="ss">:hello</span><span class="p">}</span>
<span class="c1">#hello</span>
<span class="c1"># => nil </span>
<span class="n">a</span><span class="p">.</span><span class="nf">example</span> <span class="o">-></span><span class="n">i</span><span class="p">{</span> <span class="nb">puts</span> <span class="n">i</span><span class="p">.</span><span class="nf">hello</span><span class="p">}</span>
<span class="c1">#NoMethodError: private method `hello' called for #<A:0x000000012486e0></span>
<span class="n">a</span><span class="p">.</span><span class="nf">send</span> <span class="ss">:hello</span>
<span class="c1">#NoMethodError: undefined method `hello' for #<A:0x000000012486e0></span>
</code></pre>
<p>So having something to scope methods defined within a method could be as simple as an <code>undef</code> at the end of your scope. I liked using a private method approach above which can only be defined for the singleton instance... but if you weren't that worried about it being a private method "during its execution" then you could just use <code>undef</code>.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">B</span>
<span class="k">def</span> <span class="nf">example2</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">hello2</span>
<span class="s2">"hello2"</span>
<span class="k">end</span>
<span class="n">x</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
<span class="k">ensure</span> <span class="k">undef</span> <span class="ss">:hello2</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">b</span> <span class="o">=</span> <span class="no">B</span><span class="p">.</span><span class="nf">new</span>
<span class="n">b</span><span class="p">.</span><span class="nf">example2</span> <span class="o">-></span><span class="n">i</span><span class="p">{</span><span class="nb">puts</span> <span class="n">i</span><span class="p">.</span><span class="nf">send</span> <span class="ss">:hello2</span><span class="p">}</span>
<span class="c1">#hello2</span>
<span class="c1"># => nil </span>
<span class="n">b</span><span class="p">.</span><span class="nf">example2</span> <span class="o">-></span><span class="n">i</span><span class="p">{</span><span class="nb">puts</span> <span class="n">i</span><span class="p">.</span><span class="nf">hello2</span><span class="p">}</span>
<span class="c1">#hello2</span>
<span class="c1"># => nil </span>
<span class="n">b</span><span class="p">.</span><span class="nf">hello2</span>
<span class="c1">#NoMethodError: undefined method `hello2' for #<B:0x0000000128a928></span>
</code></pre>
<p>One thing I really don't like about implementing methods/procs/lambdas within method definitions is it's not accessible directly for testing. Sometimes I have to break the logic out into external methods just to test it before getting it working and being able to re-insert the logic back in.</p> Ruby master - Feature #11665: Support nested functions for better code organizationhttps://bugs.ruby-lang.org/issues/11665?journal_id=548532015-11-15T01:17:53Zkeithrbennett (Keith Bennett)keithrbennett@gmail.com
<ul></ul><p>I completely forgot about the notation of defining instance methods within other instance method.</p>
<p>My first reaction to rediscovering this was that it would be useful for me, since it would visually communicate the relationship between the inner and outer methods. However, that communication would be a fiction, because the inner defined method is just another instance method. Furthermore, my guess is that defining an instance method every time a method is called is much more expensive than defining a lambda.</p>
<p>So I agree with what I think Matz and Hans are saying (Matz, I probably don't completely understand what you mean by '"real functions" with Java|C++ private scope."' though) -- if we can create a construct that cannot access the binding in which it was defined, and can be created only once and not every time a method is called, then that would be just about as good as nested methods (maybe even better). (As a kludge, one could do something like: @fn_x ||= -> {...}, but it would be nice not to have to define an instance variable to refer to the local function.)</p>
<p>Yusuke, we could always use define_method for the cases you describe, though I agree with you that it is probably clearer to the reader to use 'def function_name'.</p>
<p>Regarding refinements, I confess that I do not understand them yet, but I'm wondering if something as simple as a context-free function should require a relatively complex construct.</p>
<p>And Dan, regarding the inability to test lambdas (and possibly the new inner functions), I suggest using methods where testing at that level is required, but I believe there are many cases where lambdas can do low level implementation details whose behavior can be adequately tested by testing the enclosing method. (One can also extract a class for a complex method containing several lambdas, of course.)</p>
<p>-- Keith</p> Ruby master - Feature #11665: Support nested functions for better code organizationhttps://bugs.ruby-lang.org/issues/11665?journal_id=551752015-11-30T21:20:57Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul><li><strong>Related to</strong> <i><a class="issue tracker-1 status-5 priority-4 priority-default closed" href="/issues/11754">Bug #11754</a>: Visibility scope is kept after lexical scope is closed</i> added</li></ul> Ruby master - Feature #11665: Support nested functions for better code organizationhttps://bugs.ruby-lang.org/issues/11665?journal_id=551762015-11-30T21:27:03Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul></ul><p>According to <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Visibility scope is kept after lexical scope is closed (Closed)" href="https://bugs.ruby-lang.org/issues/11754">#11754</a>, this change seemed to cause an actual issue for some gems.</p>
<p>--<br>
Yusuke Endoh <a href="mailto:mame@ruby-lang.org" class="email">mame@ruby-lang.org</a></p> Ruby master - Feature #11665: Support nested functions for better code organizationhttps://bugs.ruby-lang.org/issues/11665?journal_id=615482016-11-16T15:06:51Zzotherstupidguy (mohamed fouad)zotherstupidguy@gmail.com
<ul></ul><p>Yukihiro Matsumoto wrote:</p>
<blockquote>
<p>But at least, the current behavior of nested method definition is useless. It should be made obsolete to open up the future possibility (I'd vote for warning).</p>
</blockquote>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># nested methods allow enforcing dsl constructs scopes</span>
<span class="k">def</span> <span class="nf">a</span> <span class="o">&</span><span class="n">block</span>
<span class="nb">p</span> <span class="s2">"a"</span>
<span class="k">def</span> <span class="nf">b</span> <span class="o">&</span><span class="n">block</span>
<span class="nb">p</span> <span class="s2">"b"</span>
<span class="k">def</span> <span class="nf">c</span> <span class="o">&</span><span class="n">block</span>
<span class="nb">p</span> <span class="s2">"c"</span>
<span class="nb">instance_eval</span> <span class="o">&</span><span class="n">block</span>
<span class="k">end</span>
<span class="nb">instance_eval</span> <span class="o">&</span><span class="n">block</span>
<span class="k">undef</span> <span class="ss">:c</span>
<span class="k">end</span>
<span class="nb">instance_eval</span> <span class="o">&</span><span class="n">block</span>
<span class="k">undef</span> <span class="ss">:b</span>
<span class="k">end</span>
<span class="c1"># Works</span>
<span class="n">a</span> <span class="k">do</span>
<span class="n">b</span> <span class="k">do</span>
<span class="n">c</span> <span class="k">do</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># Doesn't Work </span>
<span class="n">b</span> <span class="k">do</span>
<span class="k">end</span>
<span class="n">c</span> <span class="k">do</span>
<span class="k">end</span>
</code></pre>
<p>I would favor the simplicity of functional programming over the complex OOP alternatives. Not only I am applying this in my own gems, I also find that - philosophically - it matches the intention of constructing DSLs grammar via nesting.</p>
<p>source: <a href="https://gist.github.com/zotherstupidguy/71e45dc0cb1de7e3eb38a89931c808cf" class="external">https://gist.github.com/zotherstupidguy/71e45dc0cb1de7e3eb38a89931c808cf</a></p>