1

Using the ruby_parser and Ruby2Ruby gems, I'm writing code that keeps track of what conditions have been evaluated and what their results and parameters were. In order to keep this as simple as possible, I sometimes rewrite the AST a bit. Of course, I can only do that if I'm sure the result functions exactly the same as the original.

Am I correct in asserting that the following three Ruby snippets are equivalent in function, assuming the triple dots are replaced by a valid Ruby expression? Am I overlooking any edge cases?

case var
  when foo 
    something
  when ... 
    another_thing
  else 
    something_else
end

if foo === var 
  something
elsif ... === var
  another_thing
else  
  something_else
end

case
  when foo === var 
    something
  when ... === var 
    another_thing
  else 
    something_else
end
Confusion
  • 16,256
  • 8
  • 46
  • 71

2 Answers2

2

Those three snippets are equivalent iff var is idempotent, i.e. evaluating var multiple times has the same side-effects as evaluating it once.

So, if var really is a variable, you are safe, but remember that it can be an arbitrary expression, including a message send to a side-effecting method (like puts).

E.g.

case puts('Hello')
when 1
when 2

is not the same as

if 1 === puts('Hello')
elsif 2 === puts('Hello')

because in the latter case, "Hello" will be printed twice.

A better translation might be:

__randomly_generated_guaranteed_unique_local_variable_jhggfq45g345 = var

if foo === __randomly_generated_guaranteed_unique_local_variable_jhggfq45g345 
  something
elsif ... === __randomly_generated_guaranteed_unique_local_variable_jhggfq45g345
  another_thing
else  
  something_else
end

case
  when foo === __randomly_generated_guaranteed_unique_local_variable_jhggfq45g345
    something
  when ... === __randomly_generated_guaranteed_unique_local_variable_jhggfq45g345
    another_thing
  else 
    something_else
end
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • Good point. The code already contains a stern warning that it will not work for arguments with side-effects (for similar reasons as you outline here), but I hadn't yet realized it applies here as well. Modified the documentation accordingly. – Confusion Apr 19 '12 at 13:37
0

Yes, the three Ruby snippets are equivalent in function.

Update :

In Ruby the when of a case statement is an implicit ===. So, these 3(trimmed) are essentially the same :

case var when foo, if foo === var & case when foo === var

I shall quote the documentation a few times here.

===, == and eql? produce different results for class objects. These are overridden in other classes, like String. If foo and bar were String, you could replace the triple equals === assertion with foo == var or foo.eql? var

However, they differ for our normal classes.

=== : In the case of Class === (or Class.===), the operation will return true if the argument is an instance of the class (or subclass). For :

class A 
end
class B < A
end
b = B.new

A === b => true, b === A => false. (b.instance_of? A => false, b.instance_of? B => true)

== : At the Object level, == returns true only if obj and other are the same object. b == b => true, B.new == B.new => false

For class Object, === is effectively the same as calling ==. Fixnum === 1 => true, 1 === Fixnum => false

eql? : The eql? method returns true if the two have the same value. 1 == 1.0 => true, 1.eql? == 1.0 => false

prasvin
  • 3,009
  • 23
  • 28
  • 1
    Replacing `===` by `==` or `eql?` would lead to these snippets giving different results. Try `class A; end; class B < A; end; b = B.new; case b; when A then puts 'a'; end` and note that `A === b` is true, but that `A == b` and `A.eql? b` are decidedly false – Confusion Apr 19 '12 at 11:01
  • Sorry that I did not put more details in my answer. I did not realise that you were doing this for inherited classes & objects. I simply had an integer and a string in my mind when evaluating the functionalities of above 3 snippets. Yes, you are right, `===`, `==` and `eql?` produce different results for class objects. These are overridden in other classes, like `String`(which is what I based my answer upon). I have updated my answer to reflect on all these scenarios which, to be honest, I should have pondered upon in the first place. Sorry again – prasvin Apr 19 '12 at 12:10