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 ADE5F19E0037 for ; Sun, 27 Dec 2015 05:24:45 +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 E9135B5D8CF for ; Sun, 27 Dec 2015 05:57:16 +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 0E41C18CC7B8 for ; Sun, 27 Dec 2015 05:57:16 +0900 (JST) Received: from [221.186.184.76] (localhost [IPv6:::1]) by neon.ruby-lang.org (Postfix) with ESMTP id 165DA12054B; Sun, 27 Dec 2015 05:57:15 +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 BB62D1204F4 for ; Sun, 27 Dec 2015 05:57:11 +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=gaoWDjTvfn4lSX0hL2h3ptw5hZc=; b=IlIscSXB0XP7Lbo2QZ Z3mFUxhwwS65cIXtuxXoTknKuTaFrJgRVAXIkfm1QET3ihPFzUqjdsRKB0Vdx6tE qq1CTVKYMtnjN9qvrALFAvUZt28CCjG1UN71EKYeCM7A+IMf5viR3Qd8b2UloeLe cd9IsDAvyEuco3dno8CTumczw= Received: by filter0526p1mdw1.sendgrid.net with SMTP id filter0526p1mdw1.20491.567EFF2421 2015-12-26 20:57:08.542197923 +0000 UTC Received: from herokuapp.com (ec2-54-198-55-32.compute-1.amazonaws.com [54.198.55.32]) by ismtpd0003p1iad1.sendgrid.net (SG) with ESMTP id pWCnkjDmRFGCWzYze5HxnQ Sat, 26 Dec 2015 20:57:08.277 +0000 (UTC) Date: Sat, 26 Dec 2015 20:57:08 +0000 From: hcatlin@gmail.com To: ruby-core@ruby-lang.org Message-ID: References: Mime-Version: 1.0 X-Redmine-MailingListIntegration-Message-Ids: 47094 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/Ymy4QrNMhiuLXJG8OTL2vJD1yS41FdTXqektMbrgf/ume+LxDfHh+iAKfn6I/w fKqjGf8UqUl9TVRmkbTAan/2qwQ2w7xwk39SivPll8bi843Tr+Cs3bVIPHlneN1DtqMSLgRcRFGE3q 4MmFI9j1lQwnPB4dgiqYtnKAgMbF6sijtfBNaXpPJBSjP36mYWIWKwVZfA== X-ML-Name: ruby-core X-Mail-Count: 72498 Subject: [ruby-core:72498] [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. Sorry, forgot to add that the literal syntax would support all currently supported string-key variations that Hash does... ~~~ {{"a" => true}} {{a: true}} {{:a => true}} ~~~ Map.new would merge in standard Hashes string keys, using a sort of overwrite last-in system, thanks to the ordered nature of hashes. ~~~ m = Map.new({a: false, "a" => true}) m[:a] #=> true ~~~ An attempt to use same keys in the literal syntax would do the same thing. Optionally, you could raise a warning, as it works now with hashes with the same key. ~~~ {{a: 1, a: 2}} # => {{a: 2}} ## warning: key :a is duplicated and overwritten on line 1 ~~~ As you can see in my examples, I'm using the post-fixed colon in the inspected versions, which I think is what should be done, but invalid symbols can be shown as the following ~~~ {{"a" => 2, "-" => 3}} #=> {{a: 2, "-": 3}} ~~~ Very similar to how we handle things currently with Hashes and those keys, but tending towards displaying them with the post-fix, giving a visual clue to whether it's a Hash or a Map. Also, Maps should inherit from Hash. ~~~ {{}}.is_a? Hash #=> true {}.is_a? Map #=> false {{}}.to_h.is_a? Map #=> false # the following two are equivalent {}.to_map.is_a? Map #=> true Map.new({}).is_a? Map #=> true ~~~ That's all I can think of for the moment. ---------------------------------------- Feature #11882: Map or NamedMap https://bugs.ruby-lang.org/issues/11882#change-55776 * 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/