Proc question

Hello,

Let's say that I have created a proc like:

a = proc{ puts x } (NOT! a = proc{ |x| puts x })

In this case, x within the scope of the proc does not exist and proc 'a' is
unable to receive an argument to set it...

If I wanted to take proc 'a' and create another proc, which did include
arguments...

In fact, lets say I just wanted to grab an existing proc, modify the
parameters to include |*args|, knowing that within the existing proc, I am
grabbing my passed variables from the args array, even though they, at that
point, don't exist...

I did find the 'parameters' method on Proc and I found that when a proc has
arity of 0, parameters is an empty array, and then depending on the
argument, parameters will return something like [ [:opt, :x], [:opt, :y] ]
for |x, y| or [ [:rest, :args] ] for |*args|...

However, parameters, while it certain LOOKS like it is taking the values if
I pass (<<) the appropriate values to it, they will not actually store.

I also played around a little bit with grabbing the binding of the proc
without parameters and then trying to create the required variables within
that binding, but I didn't try too hard which is probably why it didn't
work, because in theory, that should be doable.

Is what I am attempting to do possible? Or is there a work-around that
isn't *too* hacky?

Thanks in advance,

Daniel

Hello,

Let's say that I have created a proc like:

a = proc{ puts x } (NOT! a = proc{ |x| puts x })

In this case, x within the scope of the proc does not exist and proc 'a'
is unable to receive an argument to set it...

If I wanted to take proc 'a' and create another proc, which did include
arguments...

In fact, lets say I just wanted to grab an existing proc, modify the
parameters to include |*args|, knowing that within the existing proc, I am
grabbing my passed variables from the args array, even though they, at that
point, don't exist...

I did find the 'parameters' method on Proc and I found that when a proc
has arity of 0, parameters is an empty array, and then depending on the
argument, parameters will return something like [ [:opt, :x], [:opt, :y] ]
for |x, y| or [ [:rest, :args] ] for |*args|...

However, parameters, while it certain LOOKS like it is taking the values
if I pass (<<) the appropriate values to it, they will not actually store.

The array instance returned by #parameters is just for your information.
Mutating it has not effect because clearly, you're just getting a new array
instance each time you call #parameters (likely generated from internal
state you don't have direct access to):

irb(main):001:0> a = proc { puts x }
=> #<Proc:0x007f95891baca8@(irb):1>
irb(main):002:0> a.parameters.object_id
=> 70140113542620
irb(main):003:0> a.parameters.object_id
=> 70140113536380

There is not way that I'm aware of to posthumously mutate a Proc. At best,
you can #curry the proc to get a new proc, but that's not a mutation of the
original proc, just a way to "wrap" it.

I also played around a little bit with grabbing the binding of the proc
without parameters and then trying to create the required variables within
that binding, but I didn't try too hard which is probably why it didn't
work, because in theory, that should be doable.

Yeah, with the new #local_variable_set and #local_variable_get, it does
seem like it'd work to a.local_variable_set(:x, "hello world") but as you
figured out, it doesn't, even though a.local_variable_get(:x) will indicate
the set _did_ in fact work.

Is what I am attempting to do possible? Or is there a work-around that
isn't *too* hacky?

I think someone more knowledgeable than I would have to explain exactly why
Binding#local_variable_set isn't designed to solve problems such as this
(I'm sure there's either a good technical, difficulty of implementation or
even language design reason). However, I do know it's not just that you're
doing it wrong, it just doesn't support that behavior:

From the example code in the doc for that method, you can see that even
though Binding#local_variable_set _works_ to set a variable in a binding,
actual code in the same scope that tries to reference said local variable
still raises a NameError.

···

On Wed, Jul 2, 2014 at 8:18 AM, Daniel Marvin <danielmarvin09@gmail.com> wrote:

Thanks in advance,

Daniel

--
Kendall Gifford
zettabyte@gmail.com

Frankly, I did not even fully understand what you are attempting to
do. I mean, technically yes. And there is no way I can think of that
would work. But why do you think you need that and what problem do
you want to solve with this?

Kind regards

robert

···

On Wed, Jul 2, 2014 at 4:18 PM, Daniel Marvin <danielmarvin09@gmail.com> wrote:

Let's say that I have created a proc like:

a = proc{ puts x }

In this case, x within the scope of the proc does not exist and proc 'a' is
unable to receive an argument to set it...

If I wanted to take proc 'a' and create another proc, which did include
arguments...

In fact, lets say I just wanted to grab an existing proc, modify the
parameters to include |*args|, knowing that within the existing proc, I am
grabbing my passed variables from the args array, even though they, at that
point, don't exist...

Is what I am attempting to do possible? Or is there a work-around that isn't
*too* hacky?

--
[guy, jim].each {|him| remember.him do |as, often| as.you_can - without end}
http://blog.rubybestpractices.com/

The array instance returned by #parameters is just for your information.
Mutating it has not effect because clearly, you're just getting a new array
instance each time you call #parameters (likely generated from internal
state you don't have direct access to):

irb(main):001:0> a = proc { puts x }
=> #<Proc:0x007f95891baca8@(irb):1>
irb(main):002:0> a.parameters.object_id
=> 70140113542620
irb(main):003:0> a.parameters.object_id
=> 70140113536380

Oh duh.. I think the 108 degree heat was affecting me today :slight_smile: I did
actually, more aptly, try using send :instance_variable_set, [[:req, :x],
[:req, :y]] without avail too..

I think someone more knowledgeable than I would have to explain exactly
why Binding#local_variable_set isn't designed to solve problems such as
this (I'm sure there's either a good technical, difficulty of
implementation or even language design reason). However, I do know it's not
just that you're doing it wrong, it just doesn't support that behavior:

Class: Binding (Ruby 2.1.2)

From the example code in the doc for that method, you can see that even
though Binding#local_variable_set _works_ to set a variable in a binding,
actual code in the same scope that tries to reference said local variable
still raises a NameError.

While not particularly elegant, I did find a workable solution using
instance_exec or instance_exec and binding.

a, b, c = 1, 2, 3
instance_exec(&proc{ puts a, b, c }) #=> 1\n2\n3\n
However, wrapped within some sort of container like a module or method so
we don't have to worry about variable collisions.

Or bareword-esque within a single binding
b = binding
b.define_singleton_method(:myvar){ 3 }
b.instance_exec(&proc{ puts myvar })

Definitely a good place to start!

Thanks!