Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a lambda from another lambda. Why does the order matter?

Tags:

scope

ruby

lambda

It's possible to call a lambda from another lambda:

first   = -> { 'Ok' }
second  = -> { first.call }
puts second.call
# => 'Ok'

but when the order is reversed:

first  = -> { second.call }
second = -> { 'Ok' }
puts first.call

the code fails with a NameError:

lambda_order.rb:1:in `block in <main>': undefined local variable or method `second' for main:Object (NameError)
Did you mean?  send
    from lambda_order.rb:3:in `<main>'

even though :second seems to be a local variable inside the scope of first:

first  = -> { local_variables }
second = -> { 'Ok' }
p first.call
# => [:first, :second]

I only use lambdas for golfing purposes so I'm not sure what's going on with the scope. Replacing second by a method or a constant lambda fixes the NameError. It seems related to this question but in my case, both lambdas are defined in main.

Could you please explain?

like image 339
Eric Duminil Avatar asked Jan 27 '23 09:01

Eric Duminil


1 Answers

first  = -> { defined? second }
second = -> { 'Ok' }
p first.call

results nil => The variable "second" is not defined in the lambda "first".

first  = -> { binding.receiver }
second = -> { 'Ok' }
p first.call

results main => This means that it uses the current binding of main and thus the variable "second" is defined only in the binding.

first  = -> { binding.local_variable_get(:second).call }
second = -> { 'Ok' }
p first.call

results "Ok". That's why the code also prints "Ok" when I ask for the content of the variable "second" of the binding.

Summary: The variable "second" is not defined in the lambda "first". The variable "second" is only defined in the binding. Therefore, the output of "local_variables" also returns "second" because the information is retrieved from the binding.

I also learned something myself. I hope I could help you!

like image 119
Marek Küthe Avatar answered Jan 29 '23 23:01

Marek Küthe