Why can procs be invoked with === in ruby 1.9?
Asked Answered
P

4

6

This article mentions 4 ways to invoke procs in ruby 1.9, and === is one of them. I don't understand why this would be done this way at all. Does it have any relationship to the normal meaning of === (asking if the two objects are the same object)?

irb(main):010:0> f =-> n {[:hello, n]}
=> #
irb(main):011:0> f.call(:hello)
=> [:hello, :hello]
irb(main):012:0> f === :hello
=> [:hello, :hello]
irb(main):013:0> Object.new === Object.new
=> false
irb(main):014:0> f === f
=> [:hello, #]
Palecek answered 12/3, 2012 at 21:1 Comment(0)
S
4

This is what the docs have to say:

It is to allow a proc object to be a target of when clause in the case statement.

This is a, perhaps contrived, example:

even = proc { |x| x % 2 == 0 }
n = 3
case n
when even
  puts "even!"
else
  puts "odd!"
end

It works because the case/when is basically executed like this:

if even === n
  puts "even!"
else
  puts "odd!"
end

The case/when checks which branch to execute by calling === on the arguments to when clauses, picking the first that returns a truthy value.

Despite its similarity to the equality operator (==) it not a stronger or weaker form of it. I try to think of the === operator as the "belongs to" operator. Class defines it so that you can check if an object belongs to the class (i.e. is an instance of the class or a subclass of the class), Range defines it as to check if the argument belongs to the range (i.e. is included in the range), and so on. This doesn't really make the Proc case make more sense, but think of it as a tool for making your own belongs to operators, like my example above; I defined an object that can determine if something belongs to the set of even numbers.

Streetman answered 12/3, 2012 at 21:10 Comment(1)
Oh the lengths to which ruby goes in order to have nice case/when blocks :-) String === "foo" => truePalecek
G
5

Note that === in Ruby is NOT about equality, unlike JavaScript. It is specifically used for case expressions:

case cats.length
  when 42                         # Uses 42      === cats.length
    puts :uh
  when /cool/i                    # Uses /cool/i === cats.length
    puts :oh
  when ->(n){ n.odd? || n/3==6 }  # Passes cats.length to the proc
    puts :my
end
Grandniece answered 12/3, 2012 at 21:14 Comment(1)
@louism No more odd, IMO, than using regex in a case statement (the equivalent of Enumerable#grep) or than using a block for Enumerable#select.Grandniece
S
4

This is what the docs have to say:

It is to allow a proc object to be a target of when clause in the case statement.

This is a, perhaps contrived, example:

even = proc { |x| x % 2 == 0 }
n = 3
case n
when even
  puts "even!"
else
  puts "odd!"
end

It works because the case/when is basically executed like this:

if even === n
  puts "even!"
else
  puts "odd!"
end

The case/when checks which branch to execute by calling === on the arguments to when clauses, picking the first that returns a truthy value.

Despite its similarity to the equality operator (==) it not a stronger or weaker form of it. I try to think of the === operator as the "belongs to" operator. Class defines it so that you can check if an object belongs to the class (i.e. is an instance of the class or a subclass of the class), Range defines it as to check if the argument belongs to the range (i.e. is included in the range), and so on. This doesn't really make the Proc case make more sense, but think of it as a tool for making your own belongs to operators, like my example above; I defined an object that can determine if something belongs to the set of even numbers.

Streetman answered 12/3, 2012 at 21:10 Comment(1)
Oh the lengths to which ruby goes in order to have nice case/when blocks :-) String === "foo" => truePalecek
A
2

This feature is useful in case construction, when you need to calculate something at the comparing.

is_odd  =-> n { n%2 != 0 }
is_even =-> n { n%2 == 0 }

case 5
when is_even
  puts "the number is even"
when is_odd
  puts "the number is odd"
end

=> the number is odd
Acacia answered 12/3, 2012 at 21:17 Comment(2)
Hmm... i made my edit before @theo added something similar to his answer... GMTAPalecek
@John Bachir, I don't think that your edit is good to describe the idea of lambda in case constructionAcacia
P
0

Does it have any relationship to the normal meaning of === (asking if the two objects are the same object)?

Actually, that's a common misconception about === in Ruby. It's actually not strictly for Object#object_id comparison (although that is its behavior in many common invocations). In Ruby, === is case subsumption.

Here's the description of === from Object: "Case Equality -- For class Object, effectively the same as calling #==, but typically overridden by descendants to provide meaningful semantics in case statements."

Sadly, even though it is comprised of three =, it doesn't have anything even remotely to do with equality :-D

Publishing answered 13/3, 2012 at 4:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.