Project

General

Profile

Feature #10683

fix inconsistent behavior of Kernel.Hash()

Added by recursive-madman (Recursive Madman) almost 5 years ago. Updated almost 5 years ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:67257]

Description

I find the way the global function Hash (aka Kernel.Hash) works a bit confusing.

To illustrate:

Hash(nil) #=> {}  (1)
Hash({})  #=> {}  (2)
Hash([])  #=> {}  (3)
# but
Hash([[1,2]]) #! TypeError (4)

Case (1) and (2) make perfect sense to me (calling Hash(var) when var is an optional argument defaulting to nil will always give a (possibly empty) Hash or a TypeError, which is very useful).
Case (3) however seems inconsistent, since (4) doesn't work.

To contrast this with the respective String function:

String([])  #=> "[]"
String('')  #=> ""
String({})  #=> "{}"
String(0)   #=> "0"
String(nil) #=> ""

it seems that calling String(obj) is equivalent to calling obj.to_s.

Thus I would assume Hash(obj) being equivalent to calling obj.to_h.

It is not though (calling to_h on [[1,2]] gives {1=>2}, while using Hash() raises a TypeError).

I propose to do one of the following changes:

  • either remove the special handling of [], such that only nil or a Hash are valid values to be passed to Hash(), or
  • change Hash() to call to_h on it's argument, when the argument is neither nil nor a Hash.

History

Updated by shevegen (Robert A. Heiler) almost 5 years ago

Interesting. [] is indeed treated differently than

Hash([nil])
TypeError: can't convert Array into Hash

Hash([1,2]) 
TypeError: can't convert Array into Hash

Perhaps there is a reason for [] as input is being
treated differently?

Updated by sawa (Tsuyoshi Sawada) almost 5 years ago

Since nil.to_h is {}, which equals Hash(nil), and some_hash.to_h is some_hash, which equals Hash(some_hash), your claim:

  • change Hash() to call to_h on it's argument, when the argument is neither nil nor a Hash.

is equivalent to

  • change Hash() to call to_h on it's argument.

I don't think either of your proposed options will be accepted.

Updated by recursive-madman (Recursive Madman) almost 5 years ago

I don't think either of your proposed options will be accepted.

I can see that making Hash(obj) equivalent to obj.to_h would be a major change in functionality.

The primary reasons for opening up this issue are two points:

  • The special case of [] - what does it accomplish?
  • The inconsistency between the global Hash and String functions -- String(obj) being equivalent to obj.to_s, while Hash(obj) being roughly equivalent to obj.to_hash (instead of obj.to_h)

As written elsewhere, the short conversion methods (to_i, to_s, to_h, ...) are supposed to be for explicit conversion, while the long ones (to_int, to_str, to_hash, ...) are for implicit conversion.

Now how do these global functions fit into the picture? What can a ruby developer expect them to do without having to read their individual documentation?

Also available in: Atom PDF