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

From: keystonelemur@...
Date: 2018-08-04 15:55:38 UTC
List: ruby-core #88289
Issue #14869 has been updated by baweaver (Brandon Weaver).


I would agree with `===` being more useful than `<=`, as `case`, `all?`, `grep`, and other methods use it implicitly.

This would be an amazing addition for Ruby, and would bring us closer to pattern matching syntax.

The great part about this is your implementation uses `===` to compare values as well. This makes it very flexible, and extremely useful.

**Aside / Offtopic**

This may be unrelated, and if so feel free to tell me to open another issue, but what if it worked on Objects:

```
Person = Struct.new(:id, :name, :age)
people = [{:id=>1, :name=>"Homu", :age=>13}, {:id=>2, :name=>"mami", :age=>14}].map { |p| Person.new(*p.values) }

people.grep(age: 10..13) # => [#<struct Person id=1, name="Homu", age=13>]
```

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

* 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