Tail call optimization in Ruby with trampolines
    Posted on April 17, 2015
    
The idea is taken from Clojure.
def trampoline
  f = yield
  loop do
    case f
    when Proc
      f = f[]
    else
      break f
    end
  end
end
def fact(n, acc)
  if n > 0
    -> { fact(n - 1, acc * n) }
  else
    acc
  end
end
trampoline { fact(100000, 1) }
#=> 282422940796034787429342157802453551847749492609...
def fib(a, b, n)
  if n > 0
    -> { fib(b, a + b, n - 1)}
  else
    a
  end
end
(0..9).map { |i| trampoline { fib(0, 1, i) } }
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
trampoline { fib(0, 1, 100000) }
#=> 259740693472217241661550340212759...
# Mutual recursion
def isEven(xs)
  xs.empty? || -> { isOdd(xs[1..-1]) }
end
def isOdd(xs)
  !xs.empty? && -> { isEven(xs[1..-1]) }
end
trampoline { isEven((0..10000).to_a) } #=> false
trampoline { isOdd((0..10000).to_a)  } #=> true
trampoline { isEven((0..10001).to_a) } #=> true
trampoline { isOdd((0..10001).to_a)  } #=> falseThere is also a more strict approach taken from Scala, but it seems a bit heavy for Ruby:
class Done
  attr_reader :a
  def initialize(a)
    @a = a
  end
  def result
    a
  end
end
def Done(a)
  Done.new(a)
end
class Call
  attr_reader :rest
  def initialize
    @rest = -> { yield }
  end
  def result
    next_r = rest[]
    loop do
      case next_r
      when Done
        break next_r.a
      when Call
        next_r = next_r.rest[]
      end
    end
  end
end
def Call
  Call.new {
    yield
  }
end
def isEven(xs)
  xs.empty? ? Done(true) : Call { isOdd(xs[1..-1]) }
end
def isOdd(xs)
  xs.empty? ? Done(false) : Call { isEven(xs[1..-1]) }
end
isEven((0..9999).to_a).result   #=> true
isOdd((0..9999).to_a).result    #=> false
isEven((0..10000).to_a).result  #=> false
isOdd((0..10000).to_a).result   #=> true
def fact(n, acc)
  if n > 0
    Call { fact(n - 1, acc * n) }
  else
    Done(acc)
  end
end
fact(100000, 1).result
#=> 28242294079603478742934215780245355184...