Saturday, 18 June 2011

gets gets my goat

I'm participating in a virtual study group on the book Seven languages in seven weeks. The first week has been on Ruby, which has been a liberating and and frictionless experience - except for one glitch.

One of the exercises in the book involves writing a primitive grep in Ruby. This program reads text line-by-line from stdin and echos the lines that contain a fragment specified as a command-line argument.

My first attempt looked something like this:
while line = gets
    puts line if line.match ARGV[0]
But when I tried to run it...
> ./grep.rb foo
./grep.rb:3:in `gets': No such file or directory - foo (Errno::ENOENT)
 from ./grep.rb:3
After a lot of head-scratching, I finally figured out that gets behaves differently if there are command-line arguments. From the Ruby documentation on gets:
Returns (and assigns to $_) the next line from the list of files in ARGV (or $*), or from standard input if no files are present on the command line.
So, I actually needed to do this:
while line = STDIN.gets
    puts line if line.match ARGV[0]
After becoming a little frustrated at this seemingly inexplicable overloading of gets, I took a moment to think about why it bothered me so much.

One benefit that Ruby advocates often cite about the language is its adherence to the Principle of Least Surprise. So in the case of this particular unpleasant surprise, I felt cheated.

But then I realised that the strength of my reaction was a backhanded compliment to Ruby. The gets incident stuck in my mind only because it contrasts so strongly with the rest of my experience of Ruby as a consistent and well-designed programming language.

1 comment: