From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Original-To: poffice@blade.nagaokaut.ac.jp Delivered-To: poffice@blade.nagaokaut.ac.jp Received: from kankan.nagaokaut.ac.jp (kankan.nagaokaut.ac.jp [133.44.2.24]) by blade.nagaokaut.ac.jp (Postfix) with ESMTP id 5A88019C0032 for ; Wed, 11 Nov 2015 21:52:08 +0900 (JST) Received: from voscc.nagaokaut.ac.jp (voscc.nagaokaut.ac.jp [133.44.1.100]) by kankan.nagaokaut.ac.jp (Postfix) with ESMTP id 193A3B5D87E for ; Wed, 11 Nov 2015 22:21:52 +0900 (JST) Received: from neon.ruby-lang.org (neon.ruby-lang.org [221.186.184.75]) by voscc.nagaokaut.ac.jp (Postfix) with ESMTP id 8C1CE18CC7ED for ; Wed, 11 Nov 2015 22:21:52 +0900 (JST) Received: from [221.186.184.76] (localhost [IPv6:::1]) by neon.ruby-lang.org (Postfix) with ESMTP id D463312041F; Wed, 11 Nov 2015 22:21:49 +0900 (JST) X-Original-To: ruby-core@ruby-lang.org Delivered-To: ruby-core@ruby-lang.org Received: from o2.heroku.sendgrid.net (o2.heroku.sendgrid.net [67.228.50.55]) by neon.ruby-lang.org (Postfix) with ESMTPS id 5C58E120400 for ; Wed, 11 Nov 2015 22:21:46 +0900 (JST) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sendgrid.me; h=from:to:references:subject:mime-version:content-type:content-transfer-encoding:list-id; s=smtpapi; bh=avngu/o0dD+9mQytvymaaRez/9Q=; b=xP1w2bv3usAGi1oC5u ws+pU43E0xCzGoULiTMLuqwK1fd5CXfZjvhefLSNPbNZjzG707+rHWeWRo4rgKcX AE0CXJjYLdEHgPWfXEV78Q/wO5KgorEBUK+Gsp0cM0skyzlcKiGSq1mV2PzjLeDs 6zIQS3OcfKmO50neYWuT7j+Dc= Received: by filter0460p1mdw1.sendgrid.net with SMTP id filter0460p1mdw1.5171.564340E21B 2015-11-11 13:21:38.318962174 +0000 UTC Received: from herokuapp.com (ec2-54-159-168-219.compute-1.amazonaws.com [54.159.168.219]) by ismtpd0005p1iad1.sendgrid.net (SG) with ESMTP id duPQOof8RkS3u2SqYQuTdQ for ; Wed, 11 Nov 2015 13:21:38.181 +0000 (UTC) Date: Wed, 11 Nov 2015 13:21:38 +0000 From: 6ftdan@gmail.com To: ruby-core@ruby-lang.org Message-ID: References: Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-Redmine-MailingListIntegration-Message-Ids: 46092 X-Redmine-Project: ruby-trunk X-Redmine-Issue-Id: 11665 X-Redmine-Issue-Author: keithrbennett X-Redmine-Sender: danielpclark X-Mailer: Redmine X-Redmine-Host: bugs.ruby-lang.org X-Redmine-Site: Ruby Issue Tracking System X-Auto-Response-Suppress: All Auto-Submitted: auto-generated X-SG-EID: ync6xU2WACa70kv/Ymy4QrNMhiuLXJG8OTL2vJD1yS7yPV+HHBJZnTI40xCxh6KZxNdwYNxIuXeIL6 /5Aj2onMFMqsdm+G5xlm+beeSkjcPdJ6rrEpFBaPAypowDEKH2YkTdcNFz5t3sDpjaMHR4Exli5Te0 ULX+Q70VPCAS+eWC65Rc3/dUAZvdm7FJ628i X-ML-Name: ruby-core X-Mail-Count: 71449 Subject: [ruby-core:71449] [Ruby trunk - Feature #11665] Support nested functions for better code organization X-BeenThere: ruby-core@ruby-lang.org X-Mailman-Version: 2.1.15 Precedence: list Reply-To: Ruby developers List-Id: Ruby developers List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: ruby-core-bounces@ruby-lang.org Sender: "ruby-core" Issue #11665 has been updated by Daniel P. Clark. 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 http://weblog.jamisbuck.org/2015/10/17/dynamic-def.html I 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 #6 . If that could be implemented in a meta-programming way as a method on Class, maybe as `scoped_def :m, *a, &b` you can use it anywhere. Here's an example without using refinements ~~~ruby class A def example(x) class << self private def hello "hello" end end x.call(self) ensure # if proc call above fails we don't want to leak the method class << self undef :hello end end end a = A.new a.example ->i{ puts i.send :hello} #hello # => nil a.example ->i{ puts i.hello} #NoMethodError: private method `hello' called for # a.send :hello #NoMethodError: undefined method `hello' for # ~~~ So having something to scope methods defined within a method could be as simple as an `undef` 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 use just `undef`. ~~~ruby class B def example2(x) def hello2 "hello2" end x.call(self) ensure undef :hello2 end end b = B.new b.example2 ->i{puts i.send :hello2} hello2 # => nil b.example2 ->i{puts i.hello2} #hello2 # => nil b.hello2 #NoMethodError: undefined method `hello2' for # ~~~ ---------------------------------------- Feature #11665: Support nested functions for better code organization https://bugs.ruby-lang.org/issues/11665#change-54821 * Author: Keith Bennett * Status: Open * Priority: Normal * Assignee: ---------------------------------------- The wisdom of using local variables has been internalized in all of us from the beginning of our software careers. If we need a variable referring to data that is used only in a single method, we create a local variable for it. Yet if it is logic to which we need to refer, we make it an instance method instead. In my opinion, this is inconsistent and unfortunate. The result is a bloated set of instance methods that the reader must wade through to mentally parse the class. The fact that some of these methods are used only by one other method is never communicated by the code; the reader has to discover that for him/herself. The number of possible interactions among the instance methods is one of many measures of our software's complexity. The number of possible instance method interactions is (method_count * (method_count) - 1). Using this formula, a class with 10 methods will have a complexity of 90. If 4 of those methods are used by only 1 other method, and we could move them inside those methods, the complexity would plummet to 30 (6 * (6 - 1)), a third of the original amount! While it is possible to extract subsets of these methods into new smaller classes, this is not always practical, especially in the case of methods called only by the constructor. Fortunately, we do have lambdas in Ruby, so I will sometimes create lambdas inside methods for this purpose. However, lambdas are not as isolated as methods, in that they can access and modify local variables previously defined outside their scope. Furthermore, the lambdas can be passed elsewhere in the program and modify those locals from afar! So using methods would be cleaner and safer. Another weakness of using lambdas for this purpose is that, unlike methods that are created at interpret time, lambdas are objects created at runtime -- so if a method creating 2 lambdas is called a million times in a loop, you'll need to create and garbage collect another 2 million objects. (This can be circumvented by defining the lambdas as class constants or assigning them to instance variables, but then they might as well be instance methods.) I realize that implementing this feature would be a substantial undertaking and may not be feasible at this time. That said, I think it would be useful to discuss this now so we might benefit from its implementation someday. * * * * (Much of this content is communicated in my talk on Ruby lambdas; slide show is at https://speakerdeck.com/keithrbennett/ruby-lambdas-functional-conf-bangalore-oct-2014 and YouTube video of the presentation at FunctionalConf in Bangalore at https://www.youtube.com/watch?v=hyRgf6Qc5pw.) Also, this post is also posted as a blog article at http://www.bbs-software.com/blog/2015/11/07/the-case-for-nested-methods-in-ruby/. -- https://bugs.ruby-lang.org/