Tuesday, November 12, 2013

Avoiding "SystemStackError: stack level too deep" in Ruby with TCO

In Ruby's YARV (1.9+), you can turn on tail call optimization to avoid "SystemStackError: stack level too deep".

To just turn it off for a bit of code, you can pass the options into RubyVM::InstructionSequence (new and compile methods do same thing, afaik):


RubyVM::InstructionSequence.new('def meth_name(b,c); d = b + c; puts "#{b} + #{c} = #{d}. now doing #{b+1} + #{c}..."; meth_name(b+1, c) end', nil, nil, nil, tailcall_optimization: true, trace_instruction: false).eval
Or, you can change the option for everything compiled further with RubyVM::InstructionSequence, but that probably isn't a good idea:

RubyVM::InstructionSequence.compile_option = {
  :tailcall_optimization => true,
  :trace_instruction => false
}

Note: don't forget you actually have to be doing a tail call for tail call optimization to work. It doesn't just mean that everything done in a method stops using the stack. And, yes, :trace_instruction => false is still required for it to work (even as of Ruby 2.1, afaik).

More info: see this S.O. answer and a generic implementation here.

No comments: