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 (smtp.nagaokaut.ac.jp [133.44.2.24]) by blade.nagaokaut.ac.jp (Postfix) with ESMTP id 5680919E0040 for ; Mon, 28 Dec 2015 06:41:33 +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 7C998B5D86F for ; Mon, 28 Dec 2015 07:14:07 +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 A04BA18CC7B9 for ; Mon, 28 Dec 2015 07:14:07 +0900 (JST) Received: from [221.186.184.76] (localhost [IPv6:::1]) by neon.ruby-lang.org (Postfix) with ESMTP id 91EC512058D; Mon, 28 Dec 2015 07:14:04 +0900 (JST) X-Original-To: ruby-core@ruby-lang.org Delivered-To: ruby-core@ruby-lang.org Received: from o10.shared.sendgrid.net (o10.shared.sendgrid.net [173.193.132.135]) by neon.ruby-lang.org (Postfix) with ESMTPS id B3B9D120468 for ; Mon, 28 Dec 2015 07:13:37 +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=3iYI00wOEHa+IvM3yEAdFT5SGaQ=; b=fhUB+dugHmBcR4rAWw LaBFMPEsh+sUp6Xd0gBp9OvGhbBRc7R9A+D1gTX+bTr/QikzfZB/ySb6QgKhLzsL Kt5UVXu8agzGU94bmC8TwphQqzwKcrYkxMcaJhC/UqC2uLAzsNz1HKIUpefEHkZM BJj7jSP4OoijB//4288krEnbI= Received: by filter0086p1mdw1.sendgrid.net with SMTP id filter0086p1mdw1.12300.5680628DA 2015-12-27 22:13:33.108232131 +0000 UTC Received: from herokuapp.com (ec2-54-221-74-83.compute-1.amazonaws.com [54.221.74.83]) by ismtpd0006p1iad1.sendgrid.net (SG) with ESMTP id FWfNPy4ERciylOMlJ_pBHw Sun, 27 Dec 2015 22:13:32.990 +0000 (UTC) Date: Sun, 27 Dec 2015 22:13:32 +0000 From: hcatlin@gmail.com To: ruby-core@ruby-lang.org Message-ID: References: Mime-Version: 1.0 X-Redmine-MailingListIntegration-Message-Ids: 47125 X-Redmine-Project: ruby-trunk X-Redmine-Issue-Id: 11882 X-Redmine-Issue-Author: hcatlin X-Redmine-Issue-Assignee: matz X-Redmine-Sender: hcatlin 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/Ymy4QrNMhiuLXJG8OTL2vJD1yS5XpewTl5S1RvpmkfitQIRJ9qdfgExQmtWFff TbBmgFGC/kDLPdQuRPLfYAy9EA9F0yurOZ9c//kiOSiWOnEciPw+CEniJxOmnmg3XomSl3qmJSG4X0 E4bgzTs9D/gAbCpqSwuc6eBZvxTn8G08h3Sh X-SendGrid-Contentd-ID: {"test_id":"1451254414"} X-ML-Name: ruby-core X-Mail-Count: 72526 Subject: [ruby-core:72526] [Ruby trunk - Feature #11882] Map or NamedMap 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 #11882 has been updated by Hampton Catlin. It was pointed out to me, that there is an ambiguity in the following case. ~~~ array.method {{a: 1}} # could be array.method { {a: 1} } #or #array.method(Map.new({a: 1})) ~~~ To be fair, I feel that this would be an extremely, extremely, extremely rare occurrence, though it is possible. The conflict won't happen if there are *any* named arguments passed into the block, aka... ~~~ array.map { |arg| {v: arg} } ~~~ The case where you are yielding a block and it's single-line and the value is a hash that is non-dynamic, and no spaces were placed... seems highly unlikely, though, again, possible. I can't think of any examples where you would implement a yielding block that would be a simple hash coming back, only. The cases where this might be present in code would be something like an options hash, but I've never seen that be implemented as a block, when Ruby has first-class support for named arguments as an option hash in the method signature. Aka... ~~~ configure(timeout: @timeout) # passing config via an option configure {{timeout: @timeout}} # passing in config via a block with a simple value ~~~ In these edge cases, I would say that we interpret it as a Map, and any errors that occur would be fairly obvious (wrong number of arguments, etc). It does mean that technically it's not 100% backwards compatible, but if this edge case is the one sacrifice for healing one of Ruby's most frustrating code idioms, it seems a fair trade to me. ---------------------------------------- Feature #11882: Map or NamedMap https://bugs.ruby-lang.org/issues/11882#change-55790 * Author: Hampton Catlin * Status: Open * Priority: Normal * Assignee: Yukihiro Matsumoto ---------------------------------------- Hash is one of the best features of Ruby. I remember being so pleased when I first learned Ruby to find out that *anything* could be a key and that you could do some really clever things with scripts, if you key of non-traditional elements. However, like many people, 10 years into using Ruby, I still am writing code to cast around symbols to strings and strings to symbols, just to use Hash as a more traditional dictionary, keyed with string-like values. And, explaining to a junior programmers why they broke the code by using a key of a different type... it's not the most elegant thing to have to explain over and over. Several proposals exist for how to deal with this, and all have been rejected... however it doesn't seem like it's for essential reasons, more technical or syntactic issues. Coming up with syntax is something I quite enjoy (Sass/Haml), so I thought I'd make a pitch for it. Requirements: 1) Doesn't break existing code 2) Doesn't totally destroy the parser 3) Seems simple to differentiate 4) Clear upgrade path My proposal is to introduce an entirely different type of Hash, called a Map (or NamedMap, if that's too ambiguous), that requires a string-like key. There are no other types of keys allowed on this Hash, other than either strings or symbols. Internally, each key would be considered a symbol only. ~~~ map = Map.new(a: 2, b: 3) map["a"] #=> 2 map[:a] #=> 2 ~~~ Already, we're better than HashWithIndifferentAccess, as it's clearly a bit easier to type. ;) What about a literal syntax? ~~~ map = {{a: 2}} empty_map = {{}} ~~~ As far as I can tell in the Ruby-syntax style, this should be pretty easy to distinguish syntactically from both a regular hash literal and a block. Further, as almost every method's option hash is string-keyed, you could easily define this. ~~~ def my _method(arg1, options = {{}}) end ~~~ Immediately, you could deal with your options hash, and not have to stress about if the end user has passed in strings or symbols into the options. It would be trivial to create a Map polyfill for most libraries to start using the non-literal version right away, as it would basically be HashWithIndifferentAccess, except we need to guarantee that the keys are string-like. So, to sum up, we avoid the 'breaking other people's existing code' by introducing a new data-type, the literal syntax (I think) should be fairly easy to implement, and it makes a very natural keyed data object (e.g. Javascript Objects) and brings that to Ruby. -- https://bugs.ruby-lang.org/