From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS4713 221.184.0.0/13 X-Spam-Status: No, score=-2.8 required=3.0 tests=AWL,BAYES_00, DKIM_ADSP_CUSTOM_MED,FORGED_GMAIL_RCVD,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,SPF_HELO_NONE,SPF_PASS shortcircuit=no autolearn=no autolearn_force=no version=3.4.2 Received: from neon.ruby-lang.org (neon.ruby-lang.org [221.186.184.75]) by dcvr.yhbt.net (Postfix) with ESMTP id B672A1F463 for ; Mon, 30 Dec 2019 14:41:27 +0000 (UTC) Received: from neon.ruby-lang.org (localhost [IPv6:::1]) by neon.ruby-lang.org (Postfix) with ESMTP id 320A01209D4; Mon, 30 Dec 2019 23:41:11 +0900 (JST) Received: from o1678948x4.outbound-mail.sendgrid.net (o1678948x4.outbound-mail.sendgrid.net [167.89.48.4]) by neon.ruby-lang.org (Postfix) with ESMTPS id 9CEEB1209D1 for ; Mon, 30 Dec 2019 23:41:07 +0900 (JST) Received: by filterdrecv-p3las1-5bf99c48d-8v9zg with SMTP id filterdrecv-p3las1-5bf99c48d-8v9zg-18-5E0A0C86-3E 2019-12-30 14:41:10.817271018 +0000 UTC m=+315851.116111051 Received: from herokuapp.com (unknown [3.83.47.54]) by ismtpd0050p1iad2.sendgrid.net (SG) with ESMTP id -PF1KSdXRNGcwybYYR36Ww for ; Mon, 30 Dec 2019 14:41:10.595 +0000 (UTC) Date: Mon, 30 Dec 2019 14:41:10 +0000 (UTC) From: zverok.offline@gmail.com Message-ID: References: Mime-Version: 1.0 X-Redmine-MailingListIntegration-Message-Ids: 72256 X-Redmine-Project: ruby-master X-Redmine-Issue-Id: 16435 X-Redmine-Issue-Author: zverok X-Redmine-Sender: zverok 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: =?us-ascii?Q?3be0g8093pjUjT94eiCA64csFDBI=2FmHQTWm54P5gda7N7r3++HQGPLB2Q05M6v?= =?us-ascii?Q?VIF=2FON450PaxV2wj0ZKj=2FYp+MIKEvJUUmOjPfMa?= =?us-ascii?Q?Qy0ynwYfjzm1ffs5hj4U9jURR7rR70oxLuoMcLC?= =?us-ascii?Q?WTAIvvlbCljmIDd6DWUhG1qfDD44oEmnTBkPhCm?= =?us-ascii?Q?5GulCI18S0OgoHbUyw11F+S67qx4lVnZ8XZ34cu?= =?us-ascii?Q?cNbA4rjevlh5oPI=2Fo=3D?= To: ruby-core@ruby-lang.org X-ML-Name: ruby-core X-Mail-Count: 96593 Subject: [ruby-core:96593] [Ruby master Feature#16435] Array#to_proc 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: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ruby-core-bounces@ruby-lang.org Sender: "ruby-core" Issue #16435 has been updated by zverok (Victor Shepelev). @Dan0042 > If I understand correctly, you are saying that of these: ... you consider that the first one provides some kind of clarity of understanding that you don't find in the other three and that leads to better design? That's an interesting question when put this way. What I believe about the first one that it has the most _interesting consequences_: it allows to write shorter and more DRY code by introducing _new concept_ (which leads to thinking "where it can lead us with some similar concepts"), while shortcuts like approved `_1.foo` or rejected `{.foo}` are just a "sugar". I believe the bad thing about "just a sugar" not that it is magically "considered harmful" by some higher powers, but that it competes with structural, conceptual enhancements: both can be sold to "pragmatic" language users with "look, it just more convenient", but "sugar" stops there, while new concept leads to _more_ new concepts. And here probably the main division happens: for what I can understand from this tracker and twitter and reddit last years, everybody's sure "we don't need new concepts, Ruby's ideal as a language, it just can use some speedups and maybe a few more shortcuts". I am not in this camp, what can I say. > I also have an issue with the way you repurpose array literals to play the role of hash/array accessors. That's a hack. It relies on the coincidence that they both use square brackets. That I can somewhat agree with :) (Funny thing, my mind does jump on "the other side of the road" when I am thinking about it: "yes, this **is** a hack, but it is nice!") ---------------------------------------- Feature #16435: Array#to_proc https://bugs.ruby-lang.org/issues/16435#change-83560 * Author: zverok (Victor Shepelev) * Status: Open * Priority: Normal * Assignee: * Target version: ---------------------------------------- The idea is obvious, but I couldn't find it discussed anywhere on tracker before. Please point me at the previous discussions if any. ```ruby class Array def to_proc proc { |v| v.dig(*self) } end end # Or, alternatively, see about alternatives at the end of proposal: class Array def to_proc proc { |v| v[*self] } end end ``` The implementation seems to provide clean and unambiguous collections indexing in Enumerators: ```ruby # Basic objects data, which could be obtained from JSON, CSV, Database... data = [ {name: 'John', department: {id: 1, title: 'Engineering'}, salary: 1000}, {name: 'Jane', department: {id: 1, title: 'Engineering'}, salary: 1200}, {name: 'Boris', department: {id: 2, title: 'Accounting'}, salary: 800}, {name: 'Alice', department: {id: 3, title: 'Management'}, salary: 1500} ] data.map(&[:name]) # => ["John", "Jane", "Boris", "Alice"] data.min_by(&[:salary]) # => {:name=>"Boris", :department=>{:id=>2, :title=>"Accounting"}, :salary=>800} pp data.group_by(&[:department, :title]) # {"Engineering"=> # [{:name=>"John", # :department=>{:id=>1, :title=>"Engineering"}, # :salary=>1000}, # {:name=>"Jane", # :department=>{:id=>1, :title=>"Engineering"}, # :salary=>1200}], # "Accounting"=> # [{:name=>"Boris", # :department=>{:id=>2, :title=>"Accounting"}, # :salary=>800}], # "Management"=> # [{:name=>"Alice", # :department=>{:id=>3, :title=>"Management"}, # :salary=>1500}]} # Works with arrays, too: data.map(&:values).map(&[0]) # => ["John", "Jane", "Boris", "Alice"] # And with mixes: data.group_by(&[:department, :title]).values.map(&[0, :name]) # => ["John", "Boris", "Alice"] ``` Naked structured data seems to be a common enough thing to make working with them easier. Some prior info: * Googling it around, I found the idea was first invented [back in 2014](https://thepugautomatic.com/2014/11/array-to-proc-for-hash-access/), and another one [in 2015](https://gist.github.com/geowy/39fde25ec2966f90a54b), not sure if it was proposed on the tracker. * Other proposals for `Array#to_proc` was: to call several methods in sequence [1](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/199820), [2](https://rails.lighthouseapp.com/projects/8994/tickets/1253-arrayto_proc), and to call method with argument [1](https://www.sanityinc.com/articles/adding-array-to-proc-to-ruby/), [2](https://bugs.ruby-lang.org/issues/10829), [3](https://www.rubydoc.info/github/estum/console_utils/Array:to_proc), to call several methods in parallel: [1](https://gist.github.com/shell/1120249) Honestly, I feel that proposed usage is the most frequently needed. Also, the readability of the version seems more or less straightforward: ```ruby # Existing shortcut, for example: data.map(&:keys) # Is equivalent to data.map { |x| x.keys } # ^^^^^ -- "just remove this part" # Proposed shortcut: data.map(&[:name]) # Is equivalent to data.map { |x| x[:name] } # ^^^^^ -- "just remove this part" ``` **`dig` or `[]` alternative implementations** It is up to discussion (if the whole idea holds water) whether `dig` should be used or just `[]`. The `dig` version is convenient for nested structures but slightly breaks "equivalency" shown above, and just `[]` version will allow this: ```ruby data.map(&:values).map(&[1..-1]) # => [[{:id=>1, :title=>"Engineering"}, 1000], [{:id=>1, :title=>"Engineering"}, 1200], [{:id=>2, :title=>"Accounting"}, 800], [{:id=>3, :title=>"Management"}, 1500]] ``` Maybe, for the sake of explainability, "just `[]`" should be preferred, with digging performed by other means. -- https://bugs.ruby-lang.org/