[ruby-core:124660] [Ruby Feature#21858] `Kernel#Hash` considers `to_h` too
Issue #21858 has been reported by ccmywish (Aoran Zeng). ---------------------------------------- Feature #21858: `Kernel#Hash` considers `to_h` too https://bugs.ruby-lang.org/issues/21858 * Author: ccmywish (Aoran Zeng) * Status: Open ---------------------------------------- 1. `Kernel#Integer` uses `to_int` first and `to_i` second 2. `Kernel#Array` uses `to_ary` first and `to_a` second 3. `Kernel#Hash` only uses `to_hash` I don't quite understand why there is a need for differential treatment here. I admit that maybe the only benefit of considering `to_h` secondly is that it enables multiple APIs to maintain consistency. -- https://bugs.ruby-lang.org/
Issue #21858 has been updated by ccmywish (Aoran Zeng). https://github.com/ruby/ruby/pull/16036 ---------------------------------------- Feature #21858: `Kernel#Hash` considers `to_h` too https://bugs.ruby-lang.org/issues/21858#change-116255 * Author: ccmywish (Aoran Zeng) * Status: Open ---------------------------------------- 1. `Kernel#Integer` uses `to_int` first and `to_i` second 2. `Kernel#Array` uses `to_ary` first and `to_a` second 3. `Kernel#Hash` only uses `to_hash` I don't quite understand why there is a need for differential treatment here. I admit that maybe the only benefit of considering `to_h` secondly is that it enables multiple APIs to maintain consistency. -- https://bugs.ruby-lang.org/
Issue #21858 has been updated by Dan0042 (Daniel DeLorme). +1 Actually I think this is just an oversight. #to_h was added to Struct, Hash, NilClass in Ruby 2.0, and to Array, Enumerable in Ruby 2.1; previously there was just #to_hash. And `Hash()` remained unchanged instead of adapting to the new convention. I don't think that was a conscious decision. ---------------------------------------- Feature #21858: `Kernel#Hash` considers `to_h` too https://bugs.ruby-lang.org/issues/21858#change-116258 * Author: ccmywish (Aoran Zeng) * Status: Open ---------------------------------------- 1. `Kernel#Integer` uses `to_int` first and `to_i` second 2. `Kernel#Array` uses `to_ary` first and `to_a` second 3. `Kernel#Hash` only uses `to_hash` I don't quite understand why there is a need for differential treatment here. I admit that maybe the only benefit of considering `to_h` secondly is that it enables multiple APIs to maintain consistency. -- https://bugs.ruby-lang.org/
Issue #21858 has been updated by matz (Yukihiro Matsumoto). I have no strong objection, but it is true that we have big side effect when we allow `to_h` from `Hash()` as @Dan0042 pointed out for example. What do you think? Matz. ---------------------------------------- Feature #21858: `Kernel#Hash` considers `to_h` too https://bugs.ruby-lang.org/issues/21858#change-116403 * Author: ccmywish (Aoran Zeng) * Status: Open ---------------------------------------- 1. `Kernel#Integer` uses `to_int` first and `to_i` second 2. `Kernel#Array` uses `to_ary` first and `to_a` second 3. `Kernel#Hash` only uses `to_hash` I don't quite understand why there is a need for differential treatment here. I admit that maybe the only benefit of considering `to_h` secondly is that it enables multiple APIs to maintain consistency. -- https://bugs.ruby-lang.org/
Issue #21858 has been updated by ccmywish (Aoran Zeng).
as @Dan0042 (Daniel DeLorme) pointed out for example
Just to clarify — maybe I missed something, but it seems there might be a slight misunderstanding. In his comment, Daniel mainly provided historical context about when `to_h` was introduced and suggested that the current behavior of `Hash()` was likely an oversight. I couldn't find where he pointed out a "big side effect." If there is a specific example he mentioned, I may have overlooked it; otherwise, could this possibly be referring to a different discussion? <br>
What do you think?
Speaking from my own programming habits, I almost never use conversion functions like `Array()` or `Integer()` — I tend to explicitly call `.to_xxx` methods in a more OO style. In fact, I only discovered that Ruby provides these global-style functions when I was reading the `Kernel` module documentation. Given that these functions are provided, I believe they should behave in a predictable and consistent way. Otherwise, **users are left to memorize special cases** — like the fact that `Hash()` does not consider `to_h`, unlike its counterparts. Regarding the potential side effects of this change: since I don’t widely use these conversion functions myself, it’s hard for me to assess how much impact this would have in the wider community. Perhaps it would be worthwhile to gather some usage feedbacks from other developers. ---------------------------------------- Feature #21858: `Kernel#Hash` considers `to_h` too https://bugs.ruby-lang.org/issues/21858#change-116408 * Author: ccmywish (Aoran Zeng) * Status: Open ---------------------------------------- 1. `Kernel#Integer` uses `to_int` first and `to_i` second 2. `Kernel#Array` uses `to_ary` first and `to_a` second 3. `Kernel#Hash` only uses `to_hash` I don't quite understand why there is a need for differential treatment here. I admit that maybe the only benefit of considering `to_h` secondly is that it enables multiple APIs to maintain consistency. -- https://bugs.ruby-lang.org/
Issue #21858 has been updated by mame (Yusuke Endoh). During the dev meeting discussion, @ko1, not @Dan0042, pointed out the following behavior change. It seems Matz got confused about that. ```ruby S = Struct.new(:a, :b) obj = S.new(1, 2) Hash(obj) #=> current: can't convert S into Hash (TypeError) #=> proposal: {a: 1, b: 2} ``` The behavior changes for objects that only define `to_h`. I'm not sure if this is a "big side effect", but the type coversion methods like `Kernel#Integer` are in principle strict (though this design isn't always strictly enforced), and it may raise an exception for weird input (e.g., `Integer("0x1x")` raises an exception). Therefore, there might be code that expects `Hash(struct)` to raise an exception. ---------------------------------------- Feature #21858: `Kernel#Hash` considers `to_h` too https://bugs.ruby-lang.org/issues/21858#change-116424 * Author: ccmywish (Aoran Zeng) * Status: Open ---------------------------------------- 1. `Kernel#Integer` uses `to_int` first and `to_i` second 2. `Kernel#Array` uses `to_ary` first and `to_a` second 3. `Kernel#Hash` only uses `to_hash` I don't quite understand why there is a need for differential treatment here. I admit that maybe the only benefit of considering `to_h` secondly is that it enables multiple APIs to maintain consistency. -- https://bugs.ruby-lang.org/
Issue #21858 has been updated by Dan0042 (Daniel DeLorme).
```ruby Hash(obj) #=> current: can't convert S into Hash (TypeError) #=> proposal: {a: 1, b: 2} ```
This is not really a behavior **change** but more like a backward-compatible behavior **addition/extension** It's really in the same vein as adding a method: ```ruby {a:1,b:2}.except(:b) #Ruby 2.7: undefined method `except' for {:a=>1, :b=>2}:Hash (NoMethodError) #Ruby 3.0: {:a=>1} ``` Or adding an optional parameter: ```ruby %w[a b a c].tally({}) #Ruby 3.0: wrong number of arguments (given 1, expected 0) (ArgumentError) #Ruby 3.1 => {"a" => 2, "b" => 1, "c" => 1} ``` Neither of these can really be considered a "behavior change" ---------------------------------------- Feature #21858: `Kernel#Hash` considers `to_h` too https://bugs.ruby-lang.org/issues/21858#change-116434 * Author: ccmywish (Aoran Zeng) * Status: Open ---------------------------------------- 1. `Kernel#Integer` uses `to_int` first and `to_i` second 2. `Kernel#Array` uses `to_ary` first and `to_a` second 3. `Kernel#Hash` only uses `to_hash` I don't quite understand why there is a need for differential treatment here. I admit that maybe the only benefit of considering `to_h` secondly is that it enables multiple APIs to maintain consistency. -- https://bugs.ruby-lang.org/
Issue #21858 has been updated by mame (Yusuke Endoh). If we assume that changing a call that previously raised an exception to return a value is always "a backward-compatible behavior addition," then by that logic, we could also make `Kernel#raise` return a value without issue. :-) I said `Kernel#Hash` is a strict conversion method. There could be users and existing code that rely on it raising an exception for unexpected input. That being said, I don't have a strong personal opinion on this proposal itself. I just wanted to clarify Matz's comment. ---------------------------------------- Feature #21858: `Kernel#Hash` considers `to_h` too https://bugs.ruby-lang.org/issues/21858#change-116437 * Author: ccmywish (Aoran Zeng) * Status: Open ---------------------------------------- 1. `Kernel#Integer` uses `to_int` first and `to_i` second 2. `Kernel#Array` uses `to_ary` first and `to_a` second 3. `Kernel#Hash` only uses `to_hash` I don't quite understand why there is a need for differential treatment here. I admit that maybe the only benefit of considering `to_h` secondly is that it enables multiple APIs to maintain consistency. -- https://bugs.ruby-lang.org/
Issue #21858 has been updated by Dan0042 (Daniel DeLorme). mame (Yusuke Endoh) wrote in #note-7:
I said `Kernel#Hash` is a strict conversion method.
It's true that `Integer()` is a strict conversion method, but `Array()` and `String()` are notably more lenient, and I've always seen `Hash()` as more similar to those two. Especially since `Hash()` already converts `nil` and `[]` to an empty hash, I don't think it can be considered a strict conversion method. In fact these two special cases are so weird, it's like `Hash()` already has partial support for #to_h
There could be users and existing code that rely on it raising an exception for unexpected input.
Normally, "expected input" is something that can be converted to Hash, and "unexpected input" is everything else. If Struct can be converted via `Hash()` it simply means there's a greater range of valid inputs. I would be very very surprised to see `Hash()` being used to exclude Struct objects specifically. If a developer wanted strict type validation, they would likely use `is_a?(Hash)` or `Hash.try_convert` or RBS or such. ---------------------------------------- Feature #21858: `Kernel#Hash` considers `to_h` too https://bugs.ruby-lang.org/issues/21858#change-116440 * Author: ccmywish (Aoran Zeng) * Status: Open ---------------------------------------- 1. `Kernel#Integer` uses `to_int` first and `to_i` second 2. `Kernel#Array` uses `to_ary` first and `to_a` second 3. `Kernel#Hash` only uses `to_hash` I don't quite understand why there is a need for differential treatment here. I admit that maybe the only benefit of considering `to_h` secondly is that it enables multiple APIs to maintain consistency. -- https://bugs.ruby-lang.org/
participants (4)
-
ccmywish (Aoran Zeng) -
Dan0042 (Daniel DeLorme) -
mame (Yusuke Endoh) -
matz (Yukihiro Matsumoto)