Thursday, June 23, 2016

Implicit (and bad) type conversion

Sometimes it is interesting to know what happens behind the scenes when you are programming. The way the language you are using behaves under certain circumstances.

Someone called Keith L. post the following code in a StackOverflow question (If you visit this link, don't forget to upvote my answer there. Help me build a good reputation there.) yesterday night, asking where was the type conversion, because he could see none. The code supposedly gets a string with many words and returns the longest word in the string.

Just because you can't see something, it does not mean it is not there. Sometimes things are happening behind the scenes and this is one more reason to learn well about the behavior of your language and/or framework.

The problem here lies in the fact Keith L. is assuming (wrongly) that the index of the array is passed to the block, not its elements. Then, when this method receives a string like 'We are developing a method to find the longest word in a string', in the first pass of the each block the value of the inner i variable  is 'We', not zero as he was assuming.

So, when he writes the test if sen1[i].length > sen1[grt].length he is, in fact, doing if sen1['We'].length > sen1[grt].length.

Arrays in Ruby are always indexed by integers. When he tries to get the index of an array using a string, Ruby says this is impossible because this would mean an implicit conversion of a string type to integer type. So, you cant see the type conversion, but it is there!

What would be, then, the correct way to perform this task?

Changing Keith's code just a bit we could have it running.

As you may see, we just changed a few things:

  • The name of the method was changed from LongestWord to longest_word. This is not causing the error, of course, but it is kind of violating Ruby's naming conventions. The first letter in uppercase is reserved to constants and class names;
  • The regular expression used to split the string into word is not necessary. Split does it correctly with just a space (" ") passed as parameter. And a good rule in programming is: Take away unnecessary things; and,
  • The most important of all, the cause of the error, we changed from each to each_index. This will do exactly what Keith wanted in the first time, passing the index, not the element of the array, to the block. Changing this the code runs fine.
Runs fine, of course, but it's not good yet. Keith would still be spending ten lines of code to do something that can be done with three lines.

This is the real Ruby style! And if you want a good exercise to improve your Ruby skills, try to write things in just one line as much as you can. Remember that all things in Ruby are objects, even the return values of the methods. Then you may concatenate operations like in the code above. You know the result of str.split(" ") will be an array, so you may use the max_by method directly on it. You don't need to store this result in a variable and then to use max_by on this variable.

Learn the Ruby way and your code will be shorter and better.

See you next post!