Home > Software engineering >  How can I get argument values for a given lambda from the outside, without explicitly returning its
How can I get argument values for a given lambda from the outside, without explicitly returning its

Time:05-03

I want to be able to run a lambda and get at its argument values (the values of a and b below).

I can achieve this by explicitly having the lambda return a binding, and then getting the values out of this binding:

fun = lambda { |a, b = Time.now| binding }
fun_binding = fun.call(123)
puts "A is #{fun_binding.local_variable_get(:a)} and B is #{fun_binding.local_variable_get(:b)}"
# Outputs e.g.: A is 123 and B is 2022-04-29 20:14:07  0200

Is it possible to get these values without running any code inside the lambda body? I.e. could I do

fun = lambda { |a, b = Time.now| }
fun.call(123)

and still get the a and b values somehow from outside the lambda?

(To provide context – I'm asking because I've experimented with this as a shortcut syntax for instantiating objects with ivars/readers automatically assigned. At this stage I'm more curious about what's possible than what is advisable – so I invite any solutions, advisable or not…)

CodePudding user response:

I'm not sure how advisable this is, but you can use the TracePoint class with the :b_call event to grab the lambda's binding:

fun = lambda { |a, b = Time.now| }

fun_binding = nil
TracePoint.trace(:b_call) do |tp|
    fun_binding = tp.binding
    tp.disable
end

fun.call(123)

puts "A is #{fun_binding.local_variable_get(:a)} and B is #{fun_binding.local_variable_get(:b)}"

This is not a perfect solution and will fail if one of the argument default values enters a block on it's own (since the default values are evaluated before the :b_call event is fired for the lambda), but that could be fixed with some additional filtering inside the TracePoint handler.

  • Related