Question

I have a method accepting a lambda which will be called or a string which will be evaluated. Why does the following method fail:

def test(expr)
  expr = expr.respond_to?(:call) ? expr : ->{ eval(expr) }
  expr.call
end

test 'puts 1'
# => TypeError: can't convert Proc into String

But this one work:

def test(expr)
  foo = expr
  expr = expr.respond_to?(:call) ? expr : ->{ eval(foo) }
  expr.call
end

test 'puts 1'
# => 1
Was it helpful?

Solution

In here:

expr = expr.respond_to?(:call) ? expr : ->{ eval(expr) }

You end up with a Proc in expr that is a closure over expr so when the Proc is evaluated, it tries to eval itself because the expr inside the lambda is the lambda itself.

In here:

foo  = expr
expr = expr.respond_to?(:call) ? expr : ->{ eval(foo) }

the closure is over foo which refers to the original String value of expr so there is no funky self-referential confusion and you'll end up using eval on a String.

OTHER TIPS

You are assigning lambda to variable expr. That lambda tries to eval content of variable expr (i.e. tries to eval itself). eval expects only string, that is why exception is raised.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top