Search code examples
rubycomparisonblockanonymous-functionbehavior

Why does a combined comparison fail when something is put behind it in a block statement?


I am learning Ruby for the first time and I found that inside of a block statement when using the combined comparison operator (or spaceship operator) that a statement placed before the operator will run but a statement placed afterward will cause an error. I discovered this by playing with semicolons.

list.sort { |item1, item2| puts "foo"; item1 <=> item2 } works but

list.sort { |item1, item2| item1 <=> item2; puts "foo" } fails.

I tried this outside of a block statement and the problem goes away. I then tried on multi-line block statements like

list.sort do |item1, item2|
  item1 <=> item2
  puts "foo"
end

and it has the same problem as the single line block.

Does anyone know what is going on?

The best explanation I have thought of is that <=> uses splat-like parameter functionality and is sucking in the text after item 2, but I don't know why it would happen in a block statement but not outside of one or why it is ignoring the semicolon and newlines. A cursory search hasn't turned up any useful documentation.

Edit: The error I get when using Codeacadamy.com's compiler is "comparison of String with String failed."

Edit2:

ruby -e '
> list = ["one", "two", "three"]
> list.sort { |f, s| f <=> s; puts "foo" }
> puts list
> '
foo
-e:3:in `sort': comparison of String with String failed (ArgumentError)
from -e:3:in `<main>'

Solution

  • When you do this:

    list.sort do |item1, item2|
      item1 <=> item2
      puts "foo"
    end
    

    Ruby looks at the return value of the block to determine the comparison. Since you have puts "foo" at the end of the block, the result of the custom compare will be the output of puts "foo" (which is nil). You want the comparator to be the last thing you do in the block.