Blocks/lambdas and state

1 view
Skip to first unread message

Tony Arcieri

unread,
Feb 25, 2009, 4:45:39 PM2/25/09
to re...@googlegroups.com
This e-mail concerns this ticket:

http://reia.lighthouseapp.com/projects/19319/tickets/22-class-method-is-not-seen-inside-a-block

...and we're further down the rabbit hole.

In this case, Phil is trying to invoke a method from within a block, which is a perfectly reasonable thing to do in a language like Ruby.  However, the semantics of how this should operate in a language like Reia are tricky.

What's so hard about calling a (local) method from a block, you may ask?  Well, methods in Reia differ from functions because they can access (and modify) hidden state.  This is yet another case of adding state to Erlang being, well, hard.

In order for a lambda to invoke a method, it needs to pass the current hidden state of an object to that method.  The simplest solution to this is to close over the hidden state at the time the lambda is created, then pass it to the method.  However, this means if we declare a lambda, alter the hidden state, then call the lambda, any method invocations will operate on the old state of the object and will not reflect the latest changes.

What do we do if the method "mutates" the hidden state?  Imagine we pass this lambda (or block) as an argument to a method in a different object.  The lambda/block will try to call a method in the first object, and make alterations to its state.  We've now stumbled upon a case of one object trying to alter the state of another through side effects.

My inclination as to the best way to proceed would be:

1) Allow lambdas/blocks to reference hidden state/instance variables, but close over them at the time the lambda/block is declared.  In the case of a lambda it means it will close over the state at the time the lambda is declared, and any references to instance variables or method invocations from the lambda will work off this "old version" of the state, which may be different than the state at the time the lambda is invoked.

2) Disallow assignment to instance variables within lambdas/blocks.  As soon as you're within the scope of a lambda/block "mutations" to the outer hidden state are disallowed.  This is similar to restrictions on mutating the values of outer variables.  Lambdas/blocks can only access snapshots of the state at the time they are declared.  Anything you want to do with that state from there must be handled in a pure functional manner (e.g. by returning new values you wish to bind from the lambda itself).

So... don't do this!

foo() do
  @x = 42

Do this instead:

@x = foo() do
  42

The compiler can easily detect assignment to an instance variable within a block so these sorts of errors could be caught at compile time.

3) Allow blocks/lambdas to invoke methods BUT if those methods attempt to mutate any instance variables, then crash.  This means a programmer won't know until runtime that their code is attempting to illegally "mutate" the hidden state.  Methods will still be able to access the hidden state, but they'll work off a copy of it at the time the lambda/block was declared.  However, as far as I can tell there is no safe way to allow destructive updates to the state within a lambda/block since we don't have guarantees about where the lambda will execute.

There is one particular case where I can see a lambda/block safely mutating instance variables, and that would be in a case similar to Ruby's Object#intsance_eval/instance_exec.  Here we are handing an object a lambda/block and telling it "execute this against your own state rather than mine".  In this case no ivars would be closed over at all.  Instead the block invocation would accept the object's current state and could return a new state.  This is essentially the same thing as letting a block/lambda act in place of a method.

I have not yet decided if I want to support instance_eval/instance_exec.

--
Tony Arcieri

Tony Arcieri

unread,
Feb 26, 2009, 3:22:20 AM2/26/09
to Shannon -jj Behrens, re...@googlegroups.com
On Wed, Feb 25, 2009 at 4:07 PM, Shannon -jj Behrens <jji...@gmail.com> wrote:
Ouch.

Yes, ouch.  Rubyists knock Python because its lambdas cannot alter the outer binding.  However Reia is worse in that a block/lambda cannot mutate the outer state in any way.  Anything a block/lambda wishes to accomplish must be expressed in the return value.

I don't see any way around this.  It would require letting the receiver of a block/lambda mutate the caller's state, which is in direct conflict with Erlang's shared-nothing approach.

Given my own personal experience I'm not too terribly worried.  I can't think of a case where I used a block/lambda to modify the caller's instance variables.  I certainly have used a block/lambda to modify the *receiver's* instance variables, which could be potentially supported by Reia.

--
Tony Arcieri
medioh.com

Phil Pirozhkov

unread,
Feb 28, 2009, 5:37:20 PM2/28/09
to re...@googlegroups.com
> From: Tony Arcieri <to...@medioh.com> Date: Wed, 25 Feb 2009 14:45:39 -0700

> This e-mail concerns this ticket:
> http://reia.lighthouseapp.com/projects/19319/tickets/22-class-method-is-not-seen-inside-a-block
> ...and we're further down the rabbit hole.

...and we all took the red pill

> What do we do if the method "mutates" the hidden state? Imagine we pass
> this lambda (or block) as an argument to a method in a different object.
> The lambda/block will try to call a method in the first object, and make
> alterations to its state. We've now stumbled upon a case of one object
> trying to alter the state of another through side effects.

> My inclination as to the best way to proceed would be:

Is there a way to call lambdas as local methods?


Cheers, Phil

Tony Arcieri

unread,
Feb 28, 2009, 6:10:50 PM2/28/09
to re...@googlegroups.com
On Sat, Feb 28, 2009 at 3:37 PM, Phil Pirozhkov <pi...@mail.ru> wrote:
Is there a way to call lambdas as local methods?

Not sure what you mean by that...

...although as soon as I get around to implementing it, it will be possible to do the opposite with a Python-like syntax:

lambda = obj.method
lambda(1, 2, 3)

--
Tony Arcieri
Reply all
Reply to author
Forward
0 new messages