View Issue Details

IDProjectCategoryView StatusLast Update
0000662LDMud 3.5LPC Languagepublic2009-06-17 05:11
ReporterGnomi Assigned ToGnomi  
PrioritynormalSeverityminorReproducibilityhave not tried
Status assignedResolutionopen 
Platformi686OSDebian GNU/LinuxOS Version4.0
Summary0000662: RFC: Inline closures should be able to modify outer local variables
DescriptionCurrently the values of local variables that were referenced within an inline closures are copied, so that assignments in the inline closure won't affect the local variable. This behavior applies only to local variables, not global ones.

After 0000650 it should be possible to create an lvalue of the local variable instead of copying its value, so that modifications within the inline closure would reflect on the local variable. But this change might break code and it's hard to find where.

Any opinions on this?
TagsNo tags attached.

Activities

Sorcerer

2009-06-16 07:05

updater   ~0001207

While I would prefer to have context variables referenced instead of copied (so the inline can do changes on them that will affect the original variable) I agree that changing this behaviour might break a lot of code and cause some hard-to-find inconsistencies.
At the moment you can simply pass a reference to an inline in order to be able to modify it from within.
So - for me - the perfect option would be an option (even at driver-compile-time) to switch that behaviour and in addition a pragma, that allows me to throw a warning whenever a context variable is changed within a closure. So MUDs can turn on this pragma while staying at the old behaviour and find all occurances of possible code-breaks before activating the new behaviour.

zesstra

2009-06-16 16:30

administrator   ~0001213

Mhmm. I am pretty sure, we have some code in the lib which relies on outer variables not being changed by changing the implicitly defined context variables.
A change of behaviour would require something Sorceror sketched.

I am not sure about this, I kind of like it that the context variables are independent, because you can create a whole lot of closures (with different data) from the same place which can be used in parallel without interfering.
On the other hand, you can achieve this with explicitly defined context variables or variables defined in the local scope of the closure body.
But then you have two different types of context variables: implicitly defined ones, which change some other entity in a different scope and explicitly defined ones, which don't. Sounds like a source of potential problems if you are not very careful. BTW: You may define 'foo' as explicitly defined context variable and use it. What happens, if later someone defines 'foo' in the outer scope?

zesstra

2009-06-16 16:35

administrator   ~0001214

Ah, BTW: I am also not sure, if I like the possibility to switch the behaviour with a driver-compile-time switch, because it leads to even more splintering (or fragmentation?) of the LPC language. In discussions you have to ask one more parameter about the driver configuration. Thats fine for optional efuns, timings etc. but for the language itself I don't really like it.
A pragma would be slightly better, because you can at least copy the program between different systems and it behaves the same.

fufu

2009-06-16 17:59

manager   ~0001215

Btw, if the restrictions on lvalues are lifted, the opposite idea may work as well (Gnomi: Is that right?):

int foo()
{
    int bar;
    closure f = function void () : int bar = &bar { bar = 42; };
    funcall(f);
    return bar;
}

I'm not sure where I stand on this issue; I dislike the inconsistent handling of local and global variables right now, but I also dislike the potential of breaking existing code in non-obvious ways. A pragma may be a good middle compromise, if it isn't too hard to support in the compiler. (And if we have a pragma, we don't need a compile time switch - people can pick their preferred default using the auto include string)

Gnomi

2009-06-17 00:17

manager   ~0001216

Yes, for explicit context variables you would have the choice of taking the value or the reference to another variable as Fuchur described.

Zesstra, implicit and explicit context variable differ in that way, that explicit context variables have their own variable definition - so there it is very clear, that they are variables on their own. Implicit context doesn't, that's why I don't see a problem with differentiating between them.

If you define 'foo' as an explicit context variable it has precedence over all outer scopes (normal scoping rules).

I agree that making this a pragma would be a good compromise. Issuing warnings where it would break code is very hard. (You have to track whether a variable is written by the inline closure or the outer function and after that accessed by the other one. Because the compiler can't guess the timing (the closure might be executed within or after the outer function), it has to be done at runtime or otherwise there would be a lot of false positives.)

zesstra

2009-06-17 04:04

administrator   ~0001217

Well, yes. But they remain context variables with different behaviour. If I use <a> in an inline-closure I have to check, if it is an implicit, explicit context variable, explicit context variable referencing another one, a locally defined variable or a global one. Right now, I can trust (with <a> being global as exception), that I don't alter values in different closures and the defining function. I just prefer that as a safer default the same way I appreciate that arguments are passed as values, because it separates different functional units of my program from interfering.
Although I would appreciate the possibility to explicitly create a referencing context variable by 'int ref_b = &b;' in the rare cases I need it, it is just a matter of default.
That just to explain why I like the current behaviour. I won't really resist such a change, but I see not only advantages.

Concerning the pragma and 'LPC language fragmentation': The pragma is definitely needed for an intermediate time. But we should discuss, if we need it forever. I think, in the long-term I would prefer a decision for one behaviour...

Warnings... Basically, I think what Sorcerer suggests is: warn for _every_ assignment of values to an implicit context variable at compile-time. Then the wizard has to check if that assignment would be OK for a reference to the outer variable and disable the warning pragma for that program after this check. Yes... Produces a lot of warnings. But the alternative is to check every inline-closure, which is much worse.

Gnomi

2009-06-17 04:14

manager   ~0001218

It is not sufficient to check assignments to implicit context variables, but we also have to warn on assignments to local variables that were used as context variables (closure fun() { int a = 1; closure cl = (: a :); a = 2; return cl; } - with the current behavior funcall(fun()) would return 1, with the new behavior 2.).

zesstra

2009-06-17 04:58

administrator   ~0001219

Your are right. *sigh* :-(

Sorcerer

2009-06-17 05:11

updater   ~0001220

I agree with Zesstra that a compile-time-option would lead to an undesirable fragmentation of the language, so a pragma will be the better choice.

As for argument passing: I agree that explicitly passing arguments to functions (including inlines) should remain a call-by-value. This makes things way easier for inexperienced programmers. But using a variable that I declared only once (e.g. in the local scope of the function containing the inline) for me intuitively means: altering the variable, not a copy of it which I never explicitly created. Of course, I also see the problematic created in Gnomi's last example (changing values in a closure after it was created and before it is called). Still, I would prefer implicit context to be by-reference, explicit one being to option in case I need something by-value.

Issue History

Date Modified Username Field Change
2009-06-16 06:39 Gnomi New Issue
2009-06-16 06:39 Gnomi Status new => assigned
2009-06-16 06:39 Gnomi Assigned To => Gnomi
2009-06-16 07:05 Sorcerer Note Added: 0001207
2009-06-16 16:30 zesstra Note Added: 0001213
2009-06-16 16:35 zesstra Note Added: 0001214
2009-06-16 17:59 fufu Note Added: 0001215
2009-06-17 00:17 Gnomi Note Added: 0001216
2009-06-17 04:04 zesstra Note Added: 0001217
2009-06-17 04:14 Gnomi Note Added: 0001218
2009-06-17 04:58 zesstra Note Added: 0001219
2009-06-17 05:11 Sorcerer Note Added: 0001220