Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What happens when you have a case statement with no argument and the when clauses are lambdas?

Tags:

ruby

This code does not perform as I expect:

case    
when -> { false } then "why?"        
else "This is what I expect"        
end      
# => "why?"

Neither does this

case
when ->(x) {false} then "why?"  
else "This is what I expect"
end  
# => "why?"

The first then clause is executed in both cases, which must mean that the lambda I supply to the when clause is not being called. I understand that the case equality operator === should be called on whatever the subject of the when clause is. I am wondering what goes on the other side of the === when there is no argument supplied to case. I was thinking it might be nil, but it can't be:

-> {false} === nil
# => ArgumentError: wrong number of arguments (1 for 0)

->(x) {false} === nil
# => false

This performs as expected and, if it were being executed, would lead to my expected case results or an exception. Can someone explain the results above? It seems that the case equality operator isn't being used at all, and yet the first when clause is evaluating to true. BTW, I am doing this because the output of a case can be used for variable assignment and it is less wordy then having several elsif clauses. I would like to be able to use arbitrary Procs in a case statement with no argument.

like image 489
Sean Mackesey Avatar asked Jan 21 '13 23:01

Sean Mackesey


2 Answers

case    
when -> { false } then puts "why?"        
else puts "This is what I expect"        
end

case    
when 'cat' then puts "why?"        
else puts "This is what I expect"        
end

case    
when -> { false }.call then puts "why?"        
else puts "This is what I expect"        
end

outputs:

why?
why?
This is what I expect

As The Pickaxe ( http://pragprog.com/book/ruby3/programming-ruby-1-9 ) says, there are two forms of case statement.

The first allows a series of conditions to be evaluated, executing code corresponding to the first condition that is true: case when ‹ boolean-expression ›+ ‹ then › ...

The second form of a case expression takes a target expression following the case keyword. case target when ‹ comparison ›+ ‹ then › ...

In your case (case without target) any expression that is not false or nil (such as a Proc or the string 'cat') evaluates to true. The Proc is not executed, unless you call it.

like image 77
user1852994 Avatar answered Sep 27 '22 16:09

user1852994


You can use a case statement without a parameter to act similar to an if statement. For example:

case
when x > 0 then puts "positive"
else puts "negative"
end

You're assuming that it's trying to compare to nil, which isn't the case. Rather, when there's no parameter, the case statement is only testing for a "truthy" value (anything except nil and false). So when it hits your first when statement, it's checking to see if your Proc (meaning the actual ruby Proc object, not the results of executing your Proc) is nil or false, which isn't. Since it's "truthy", that code gets executed.

Try this, and you'll notice the Proc never even gets called/executed (you'll only see "bar", not "foo"):

case
when -> { puts 'foo' } then puts 'bar'
else puts "This line will never be printed"
end
like image 43
Dylan Markow Avatar answered Sep 27 '22 15:09

Dylan Markow