weakish

Quirks of Ruby

Foreword

Ruby advertises its “The Least Surprise” principle. If you are unsure about something on Ruby, you can guess and try, and it usually works, except for some quirks mentioned below.

If you prefer reading a tutorial before diving into Ruby, I would recommend why’s guide to Ruby, which may be outdated, but definitely poignant.

begin ... end while

begin puts "code executed" end while false
puts "code not executed" while false

This is really anti-intuitive. And the creator of Ruby said not using this.

Don’t use it please. I’m regretting this feature, and I’d like to remove it in the future if it’s possible.

matz

Unfortunately, this feature still exists in Ruby 3.3.

Proc.new

The return statement in proc created by Proc.new will not only return control just from itself, but also from the method enclosing it.

def some_method
	myproc = Proc.new {return "End."}
	myproc.call

	puts "This will not get executed!"
end
some_method

Well, you can argue that Proc.new inserts code into the enclosing method, just like block. But Proc.new creates an object, while block are part of an object.

And there is another difference between lambda and Proc.new. That is their handling of (wrong) arguments. Lambda complains about it, while Proc.new ignores extra arguments or considers absence of arguments as nil.

irb(main):021:0> l = -> (x) { x.to_s }
=> #<Proc:0x8b63750@(irb):21 (lambda)>
irb(main):022:0> p = Proc.new { |x| x.to_s}
=> #<Proc:0x8b59494@(irb):22>
irb(main):025:0> l.call
ArgumentError: wrong number of arguments (0 for 1)
        from (irb):21:in `block in irb_binding'
        from (irb):25:in `call'
        from (irb):25
        from /usr/bin/irb:11:in `<main>'
irb(main):026:0> p.call
=> ""
irb(main):049:0> l.call 1, 2
ArgumentError: wrong number of arguments (2 for 1)
        from (irb):47:in `block in irb_binding'
        from (irb):49:in `call'
        from (irb):49
        from /usr/bin/irb:11:in `<main>'
irb(main):050:0> p.call 1, 2
=> "1"

BTW, proc in Ruby 1.8 creates a lambda, while in Ruby 1.9+ behaves like Proc.new, really confusing.

def does not create closures.

Closures are simple in Python:

def a(x):
  def b():
    return x
  b()

This won’t work in Ruby.

def a(x)
  def b
    x
  end
  b
end
a(1) # in `b': undefined local variable or method `x' for main (NameError)

In Ruby, def starts a new scope, without access to outer variables. Only @var and $var can be accessed. And no extern keyword like in C.

Lambda and define_method do create closure though:

def a(x)
  b = ->{ x }
  b.call
end
a(1) # 1

def c(x)
  define_method(:d) { x }
  d
end
c(1) # 1

In Ruby 1.9, define_method is not available in main Object, you can use define_singleton_method instead.