Forum: Ruby Proc question

3be56d3ee0ad82d375e3e56d65b51d78?d=identicon&s=25 Daniel Marvin (meta)
on 2014-07-02 16:18
(Received via mailing list)
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
94cee02d73877ef5e6dfb04afb1fe324?d=identicon&s=25 Kendall Gifford (zettabyte)
on 2014-07-03 01:18
(Received via mailing list)
On Wed, Jul 2, 2014 at 8:18 AM, Daniel Marvin <danielmarvin09@gmail.com>
wrote:

> arguments...
>
> 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:

http://www.ruby-doc.org/core-2.1.2/Binding.html#me...

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.
3be56d3ee0ad82d375e3e56d65b51d78?d=identicon&s=25 Daniel Marvin (meta)
on 2014-07-03 05:25
(Received via mailing list)
> irb(main):003:0> a.parameters.object_id
> => 70140113536380
>

Oh duh.. I think the 108 degree heat was affecting me today :-) 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:
>

> http://www.ruby-doc.org/core-2.1.2/Binding.html#me...
>
> 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!
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (robert_k78)
on 2014-07-03 13:05
(Received via mailing list)
On Wed, Jul 2, 2014 at 4:18 PM, Daniel Marvin <danielmarvin09@gmail.com>
wrote:

> 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?

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
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.