[ruby-core:88358] [Ruby trunk Feature#14869] Proposal to add Hash#===

From: keystonelemur@...
Date: 2018-08-08 20:51:40 UTC
List: ruby-core #88358
Issue #14869 has been updated by baweaver (Brandon Weaver).


I had mentioned this in the `Array#===` topic for consideration: https://bugs.ruby-lang.org/issues/14916#note-6

In the comparison, we are returning false if the other value is not a Hash:

(line 4011)
```ruby
if (!RB_TYPE_P(hash2, T_HASH)) return Qfalse;
```

Much like the suggestion in the Array topic, would it be a good idea to leverage `to_hash`? In Ruby this means that an object behaves like a hash, allowing us duck-typing and more flexibility.

Consider an object type that responds to `to_hash` with its instance properties, we could use the same method of querying with a minimal speed penalty for coercion.

We could first check if something responds to `to_hash` if it is not already a Hash. If it does, we can coerce and operate on that value, if not we've not incurred much of a speed penalty because `Hash === Hash` would never go into that conditional.

----------------------------------------
Feature #14869: Proposal to add Hash#===
https://bugs.ruby-lang.org/issues/14869#change-73386

* Author: osyo (manga osyo)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
## ๆฆ‚่ฆ

`Hash#===` ใ‚’่ฟฝๅŠ ใ™ใ‚‹ๆๆกˆใซใชใ‚Šใพใ™ใ€‚


## ไป•ๆง˜

ใƒฌใ‚ทใƒผใƒใฎใ‚ญใƒผใฎ่ฆ็ด ใจๅผ•ๆ•ฐใฎใƒใƒƒใ‚ทใƒฅใฎใ‚ญใƒผใฎ่ฆ็ด ใ‚’ `#===` ใงๆฏ”่ผƒใ—ใฆใ€ๅ…จใฆใŒ็œŸใชใ‚‰ `true` ใ‚’่ฟ”ใ—ใ€ใใ†ใงใชใ„ใชใ‚‰ `false` ใ‚’่ฟ”ใ™ใ€‚
ใพใŸใ€ใƒฌใ‚ทใƒผใƒใŒ็ฉบใฎใƒใƒƒใ‚ทใƒฅใฎๅ ดๅˆใ€ๅผ•ๆ•ฐใŒ็ฉบใฎใƒใƒƒใ‚ทใƒฅใชใ‚‰ `true` ใ‚’่ฟ”ใ—ใ€ใใ†ใงใชใ„ใชใ‚‰ `false` ใ‚’่ฟ”ใ™ใ€‚

```ruby
user = { id: 1, name: "homu", age: 14 }

# name ่ฆ็ด ใŒ data ใซใ‚ใ‚‹ใฎใง true
p ({ name: "homu" } === user)
# => true

# ่ค‡ๆ•ฐใฎ่ฆ็ด ใŒใ‚ใฃใฆใ‚‚ OK
p ({ id: 1, name: "homu", age: 14 } === user)
# => true

# name ่ฆ็ด ใŒ user ใซใ‚ใ‚‹ใŒใ€ๅ€คใŒ้•ใ†ใฎใง false
p ({ name: "name" } === user)
# => false

# ใ‚ญใƒผใฎ่ฆ็ด ใŒๅผ•ๆ•ฐใซใชใ„ใฎใง false
p ({ number: 42 } === user)
# => false

# 1ใคใงใ‚‚ใ‚ญใƒผใฎ่ฆ็ด ใŒใชใ„ๅ ดๅˆใ‚‚ false
p ({ id: 1, name: "homu", number: 42 } === user)
# => false

# ใƒฌใ‚ทใƒผใƒใŒ็ฉบใฎใƒใƒƒใ‚ทใƒฅใชใ‚‰ false
p ({} == user)
# => false

# ็ฉบใฎใƒใƒƒใ‚ทใƒฅๅŒๅฃซใชใ‚‰ true
p ({} == {})
# => true

# ๅผ•ๆ•ฐใŒใƒใƒƒใ‚ทใƒฅไปฅๅค–ใชใ‚‰ false
p ({ id: 42 } == 42)
# => false

# #=== ใงๆฏ”่ผƒใ—ใฆใ„ใ‚‹ใฎใงใ“ใ†ใ„ใ†ใ“ใจใ‚‚ใงใใ‚‹
p ({ name: /^h/ } === user)
# => true

p ({ age: (1..20) } === user)
# => true

p ({ age: Integer } === user)
# => true
```


## ใƒฆใƒผใ‚นใ‚ฑใƒผใ‚น

### ใƒใƒชใƒ‡ใƒผใ‚ทใƒงใƒณ

case-when ใงใฏ `===` ใ‚’ไฝฟ็”จใ—ใฆๅ€คใ‚’ๆฏ”่ผƒใ—ใฆใ„ใ‚‹ใฎใงใ€`Hash#===` ใ‚’ๅˆฉ็”จใ™ใ‚‹ใ“ใจใงๆฌกใฎใ‚ˆใ†ใซๆกไปถๅˆ†ๅฒใ‚’่กŒใ†ใ“ใจใŒๅ‡บๆฅใ‚‹ใ€‚

```ruby
def validation user
	case user
	# name ใซๅฏพใ™ใ‚‹ใƒใƒชใƒ‡ใƒผใ‚ทใƒงใƒณ
	when { name: /^[a-z]/ }
		raise "ๅๅ‰ใฎๅ…ˆ้ ญใŒๅฐๆ–‡ๅญ—ใฎๅ ดๅˆใฏ็™ป้Œฒใงใใพใ›ใ‚“"
	# age ใซๅฏพใ™ใ‚‹ใƒใƒชใƒ‡ใƒผใ‚ทใƒงใƒณ
	when { age: (0..20) }
		raise "0ใ€œ20ๆญณใฏ็™ป้Œฒใงใใพใ›ใ‚“"
	# ๅ„่ฆ็ด ใŒไปปๆ„ใฎใ‚ฏใƒฉใ‚นใฎใ‚คใƒณใ‚นใ‚ฟใƒณใ‚นใ‹ใฉใ†ใ‹ใฎใƒใƒชใƒ‡ใƒผใ‚ทใƒงใƒณ
	when { id: Integer, name: String, age: Integer }
		true
	else
		false
	end
end

# ๆกไปถใ‚’ๆบ€ใŸใ—ใฆใ„ใ‚‹ใฎใง OK
mami = { id: 1, name: "Mami", age: 21 }
validation mami
# => true

# name ใŒๅฐๆ–‡ๅญ—ใ‹ใ‚‰ๅง‹ใพใฃใฆใ„ใ‚‹ใฎใง NG
mado = { id: 2, name: "mado", age: 13 }
validation mado
# => ๅๅ‰ใฎๅ…ˆ้ ญใŒๅฐๆ–‡ๅญ—ใฎๅ ดๅˆใฏ็™ป้Œฒใงใใพใ›ใ‚“ (RuntimeError)

# age ใŒ 0ใ€œ20ๆญณไปฅๅ†…ใชใฎใง NG
homu = { id: 3, name: "Homu", age: 14 }
validation homu
# => 0ใ€œ20ๆญณใฏ็™ป้Œฒใงใใพใ›ใ‚“ (RuntimeError)
```

### `Enumerable#grep`

`Enumerable#grep` ใฏๅ†…้ƒจใง `===` ใ‚’ไฝฟ็”จใ—ใŸๆฏ”่ผƒใ‚’่กŒใฃใฆใ„ใ‚‹ใฎใงใ€ๆฌกใฎใ‚ˆใ†ใซไปปๆ„ใฎ Hash ใฎใ‚ญใƒผใฎ่ฆ็ด ใซๅฏพใ—ใฆๆคœ็ดขใ‚’่กŒใ†ใ“ใจใŒๅ‡บๆฅใ‚‹ใ€‚

```ruby
data = [
	{ id: 1, name: "Homu", age: 13 },
	{ id: 2, name: "mami", age: 14 },
	{ id: 3, name: "Mado", age: 21 },
	{ id: 4, name: "saya", age: 14 },
]

# ็‰นๅฎšใฎ่ฆ็ด ใŒๅซใพใ‚Œใฆใ„ใ‚‹ Hash ใฎใฟใ‚’็ตžใ‚Š่พผใ‚€
p data.grep(name: /m/)
# => [{:id=>1, :name=>"Homu", :age=>13}, {:id=>2, :name=>"mami", :age=>14}]

p data.grep(age: (1..20))
# => [{:id=>1, :name=>"Homu", :age=>13}, {:id=>2, :name=>"mami", :age=>14}, {:id=>4, :name=>"saya", :age=>14}]
```


## ่ฃœ่ถณ1๏ผš `==` ใงใฏใชใใฆ `===` ใงๆฏ”่ผƒใ™ใ‚‹็†็”ฑ

* `===` ใ‚’ไฝฟ็”จใ™ใ‚‹ใ“ใจใงใ‚ˆใ‚Š็ดฐใ‹ใ„ใƒปๆŠฝ่ฑก็š„ใชๆกไปถใ‚’ๆŒ‡ๅฎšใ™ใ‚‹ใ“ใจใŒๅ‡บๆฅใ‚‹
  * `Class` ใ‚„ `Regexp`ใ€`Proc` ใชใฉใงๆฏ”่ผƒใ™ใ‚‹ใ“ใจใŒๅ‡บๆฅใ‚‹
* ๅ†…้ƒจใง `===` ใ‚’ไฝฟ็”จใ—ใฆใ„ใ‚‹ๅ ดๅˆใ€ `==` ใงๆฏ”่ผƒใ—ใŸใ„ๅ ดๅˆใฏ `obj.method(:==)` ใ‚’ๆธกใ›ใฐๅฎŸ็พๅ‡บๆฅใ‚‹ใŒใ€ใใฎ้€†ใฏๅ‡บๆฅใชใ„
  * ๅ†…้ƒจใง `==` ใ‚’ไฝฟ็”จใ—ใฆใ„ใ‚‹ๅ ดๅˆใ€ `===` ใงๆฏ”่ผƒใ—ใ—ใŸใใฆใ‚‚ๅ‡บๆฅใชใ„


## ่ฃœ่ถณ2๏ผš ็ฉบใฎใƒใƒƒใ‚ทใƒฅใฎๆฏ”่ผƒใซ้–ขใ—ใฆ

* `Object#===` ใฎๅ ดๅˆใ ใจ `{} === 42` ใŒไพ‹ๅค–ใงใฏใชใใฆ `false` ใ‚’่ฟ”ใ—ใฆใ„ใŸใฎใงใ€`Hash#===` ใ‚‚ `false` ใ‚’่ฟ”ใ™ใ‚ˆใ†ใซใ—ใŸ
  * `{} === {}` ใŒ `true` ใ‚’่ฟ”ใ™ใฎใ‚‚ๅŒๆง˜ใฎ็†็”ฑใซใชใ‚Šใพใ™
  * ใ“ใ‚Œใซใ‚ˆใ‚Šไปฅไธ‹ใฎใ‚ˆใ†ใชๆ—ขๅญ˜ใฎใ‚ณใƒผใƒ‰ใ‚‚ไบ’ๆ›ๆ€งใ‚’ๅฃŠใ™ใ“ใจใชใๅ‹•ไฝœใ™ใ‚‹ใ‹ใจๆ€ใ„ใพใ™

```ruby
def check n
	case n
	when {}
		"Empty Hash"
	when []
		"Empty Array"
	when 0
		"Zero"
	else
		"Not empty"
	end
end

p check({})   # => "Empty Hash"
p check([])   # => "Empty Array"
p check(0)    # => "Zero"
p check({ name: "mado" })   # => "Not empty"
```

ไปฅไธŠใ€`Hash#===` ใซ้–ขใ™ใ‚‹ๆๆกˆใซใชใ‚Šใพใ™ใ€‚
ๆŒ™ๅ‹•ใซ้–ขใ—ใฆ็–‘ๅ•็‚นใ‚„ๆ„่ฆ‹ใชใฉใ”ใ–ใ„ใพใ—ใŸใ‚‰ใ‚ณใƒกใƒณใƒˆ้ ‚ใ‘ใ‚‹ใจๅŠฉใ‹ใ‚Šใพใ™ใ€‚


---Files--------------------------------
hash_eqq.patch (3.54 KB)


-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>

In This Thread

Prev Next