From: "matz (Yukihiro Matsumoto) via ruby-core" Date: 2026-07-10T00:29:53+00:00 Subject: [ruby-core:126015] [Ruby Feature#22108] Computed hash keys with (expr): syntax Issue #22108 has been updated by matz (Yukihiro Matsumoto). Status changed from Open to Rejected I am against this proposal. The value of the label syntax (`key:`) is its simple reading: a colon-terminated key is always a Symbol. `(expr):` breaks this invariant, so readers would have to check for parentheses to know the type of the key. That is a real cost in readability, for little gain, since `expr => value` already works, is short, and is unambiguous. Also, I have no intention of deprecating `=>` in hash literals. It is not a legacy syntax to be retired; it is the general form, and the colon syntax is a shorthand for the common Symbol case. Matz. ---------------------------------------- Feature #22108: Computed hash keys with (expr): syntax https://bugs.ruby-lang.org/issues/22108#change-117994 * Author: yertto (_ yertto) * Status: Rejected ---------------------------------------- > [!WARNING] > This has potentially been obsoleted by [Feature #22111](https://bugs.ruby-lang.org/issues/22111) # Computed hash keys with `(expr):` syntax Allow `{ (expr): value }` as a computed hash key. Almost 20 years in the making, the missing puzzle piece for Hash's "new" colon notation: ```ruby h = { name: "symbol shorthand", # Ruby 1.9+ "quoted label": "symbol label", # Ruby 2.2+ (Feature #4935) value_omission: , # Ruby 3.1+ (Feature #14579) (expr): "computed", # THIS PROPOSAL } ``` ## Motivation Ruby has several colon-based hash key syntaxes but the `=>` ("hash rocket") is still needed for non-symbolic keys. Adding `(expr):` is a baby step toward allowing all-colon hashes: ```ruby ## Before -- mixed styles n = 42; { key1: "symbol", "key-#{2}": "quoted symbol", RUBY_VERSION:, n => "bar" }.keys # => [:key1, :"key-2", :RUBY_VERSION, 42] ## After -- uniform colon syntax n = 42; { key1: "symbol", "key-#{2}": "quoted symbol", RUBY_VERSION:, n : "bar" }.keys # => [:key1, :"key-2", :RUBY_VERSION, 42] ``` (And maybe a step closer to one day retiring our old friend "hash rocket" from Hashes entirely?) ### Completing the colon family | Example | Ruby | Feature | Key type | |---------|------|---------|----------| | `{ name: value }` | [v1.9](https://docs.ruby-lang.org/en/3.4/NEWS/NEWS-1_9_1.html) | | Symbol | | `{ "quoted label": value }` | [v2.2](https://docs.ruby-lang.org/en/3.4/NEWS/NEWS-2_2_0.html) | [Feature #4935](https://bugs.ruby-lang.org/issues/4935) | Symbol (quoted label) | | `{ value_omission: }` | [v3.1](https://docs.ruby-lang.org/en/3.4/NEWS/NEWS-3_1_0_md.html) | [Feature #14579](https://bugs.ruby-lang.org/issues/14579) | Value omission | | `{ (expr): value }` | *???* | [Feature #22108](https://bugs.ruby-lang.org/issues/22108) | Computed | ### Readability for computed-key-heavy code Code that builds dynamic hashes currently forces a style break midway through a literal. This is especially noticeable with interpolated keys, numeric keys, or variable-driven keys. ### Reducing `=>` overloading The `=>` token is now being used to serve more purposes than just the Hash literal (aka "Hash rocket") it was originally used for. - rightward assignment ``` expr => var ``` - pattern capture ``` case {name: "Alice", role: "admin"} in {name: String => name, role:} # capture the name String value into `name` p name # => "Alice" end ``` - rescue variables ``` rescue SomeExceptionClass => e ``` ``` rescue => e ``` Reducing its use in Hashes simplifies the language, especially for newcomers. ## Design ### `(expr):` vs `[expr]:` Parenthesized expressions were chosen over square bracket delimited: - Idempotent wrapping: `((x))` = `(x)`, but `[[x]]` != `[x]` (nested array) - Array keys: `{(["a"]): "b"}` reads naturally vs `{[["a"]]: "b"}` requiring extra wrapping - `jq` precedent: the JSON query language uses identical `{("a"+"b"): 59}` syntax (since [jq 1.6](https://jqlang.org/manual/v1.6/#object-construction), 2018) - Parentheses signals a grouping or "evaluate this", whereas square brackets signals an Array/container ### Improving on JavaScript JavaScript's "[computed property names](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#computed_property_names)" `{ [expr]: value }` were introduced in ECMAScript 2015 (ES6), 3 years before the jq version. However in Ruby, `[]` already means Array literal and method call. Rather than overloading `[]` further, `(expr):` (from jq) improves on the JavaScript design. Parentheses naturally signal evaluation, `():` inside `{}` is unambiguous to the lexer, and it matches the well-established jq convention. ### Edge cases handled - `(expr) : value` (space before colon) -- *syntax error* (consistent with `name : value`) - `{ (expr): }` (value omission) -- *syntax error* (runtime expression, no compile-time local to infer) - Mixed styles: `{ (1): "one", two: "two", "three": "three" }` -- *valid* - Nested parens: `{ ((1 + 2) * 3): "nested" }` -- *valid* - Nested hashes: `{ ({(1): "one"}): "two" }` -- *valid* ## Implementation Three changes to `parse.y` (Lrama LALR(1) parser, zero new conflicts): 1. Lexer: emit `tLABEL_END` when `EXPR_ENDFN` state, no space before `:`, and inside `brace_nest > 0` 2. Grammar: new `assoc` alternative -- `tLPAREN compstmt ")" tLABEL_END arg_value` 3. Precedence: `%nonassoc ')'` to disambiguate Reference implementation: [feature/computed-hash-keys](https://github.com/ruby/ruby/pull/17299) PR on GitHub. ## Historical context A version of this was discussed on ruby-core in October 2007 (as part of ["General hash keys for colon notation"](https://public-inbox.org/ruby-core/E1ImQBE-00033s-IZ@x31/T/), murphy). Unfortunately it was brought up during the v1.9 feature freeze, but it looks like [Matz's invitation to discuss for v2.0](https://public-inbox.org/ruby-core/E1ImQA8-00033T-8c@x31/) didn't end up going anywhere. Since then, `"quoted label":` (Ruby 2.2, [Feature #4935](https://bugs.ruby-lang.org/issues/4935)) and value omission (Ruby 3.1, [Feature #14579](https://bugs.ruby-lang.org/issues/14579)) have expanded the colon family, making the computed-key gap more conspicuous. Meanwhile, jq introduced the identical `{("a"+"b"):59}` syntax in [jq 1.6](https://jqlang.org/manual/v1.6/#object-construction) (2018), demonstrating real-world viability. ## Open questions - Is this syntax acceptable to the community? - Is the jq precedent compelling enough to address the "no language does this" objection? ## Future directions All colon-based key syntaxes would now be available. This opens up the possibility of eventually deprecating `=>` from Hash literals (while keeping it for rescue, pattern matching, and rightward assignment). This proposal does not require that change - it is simply the enabling step, and any deprecation timeline could be a separate discussion. -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/