Monday, February 09, 2009

Ruby Quirks

Ruby is a fun language, but here are a few things that tripped up this n00b whilst stumbling along the learning curve.

Why does string[n] return an integer character code instead of the character? Well, Ruby has no such thing as a character (prior to 2.0, anyway?), so then why not a string of length 1?

>> a = "asdf"
=> "asdf"
>> a[0]
=> 97

Either of these works, although they look a little funny:

>> a[0...1]
=> "a"
>> a[0..0]
=> "a"

Also, a[0].chr works. The integer.chr method is described as follows:

int.chr => string
Returns a string containing the ASCII character represented by the receiver‘s value.

It's non-obvious how to iterate through the characters of a string. The string.each_char is listed in the Ruby core 1.8.6 ruby-docs, but, confusingly, you have to require "jcode" for it to work. Maybe I'm just confused about whether core means "loaded by default" or "included in the Ruby distribution".

Two toString methods?In place of object.toString() Ruby has two methods: to_s and inspect. When coercion to a string is required, to_s is called. Docs for inspect say this:

Returns a string containing a human-readable representation of obj. If not overridden, uses the to_s method to generate the string.

If, then, else, elsif

If statements are confusing...

if x==123 then
   puts 'wazoo'

# then is optional, as long as you have the line break
if x==123
   puts 'wazoo'

For one liners, then is required. Or colon, if you prefer.

if x==123 then puts 'wazoo' end
if x==123 : puts 'wazoo' end

Curly braces seem not to work at all for if statements. For more curly brace related philosophy and WTFs see this issue. DON'T DO THIS:

if x==123 { puts 'qwer' }

Finally, would someone tell all these languages that crawl out of the primordial Bourne-shell ooze that neither elif nor elsif means jack shit in the english language?!?!?! (sputter... rant... fume...)

if x==123
  puts 'wazoo'
elsif x==456
  puts 'flapdoodle'
  puts 'foo'

An if statement is an expression and returns a value, but ruby also offers the good old ternary operator.

Require vs. Load

There are two ways to import code in Ruby, require and load. See the ruby docs for Kernel (require and load).

Defined? and nil?

Ruby has nil instead of null. Ok, and unlike Java's null, nil is a real object. I appreciate the difference between nil and undefined, but I wouldn't have guessed that defined? nil would return "nil". Not to be confused with a truly undefined variable, in which case defined? asdf returns nil. The pickaxe book explains the other strange return values of defined?. Then, there's nil?.

>> asdf.nil?
NameError: undefined local variable or method `asdf' for main:Object
 from (irb):47
 from :0
>> asdf = nil
=> nil
>> asdf.nil?
=> true

Command line arguments

Just the array ARGV. Not a quirk. Good.


Return behaves oddly; to exit a script you use Kernel::exit(integer). Trying to return 1 instead causes a LocalJumpError whatever that means?? Trying to return from a code block returns from the surrounding context. That hurts my head.

Ruby Exception Handling

Ruby's equivalent of try-catch-finally is begin-rescue-ensure-end.

No Boolean

There's no Boolean class in Ruby. Instead, there's TrueClass and FalseClass. So, what type of value does the logical expression p ^ q produce? Everything has an implicit boolean value, which is true for everything except false and nil.

List operations

(see also Array and Enumerable)


1 comment:

  1. Ruby 1.9 has addressed some of this. There is now some syntactic sugar that at least emulates true named parameters.

    Also, the list of what's changed says:

    Strings are also no longer enumerable, and no longer support each, which is replaced by the each_line and each_byte methods.

    This change also seems to have affected each_char, which does what it is supposed to do with no requires required.

    And it has a new VM, new regex engine, and it's faster. Whoohoo.