Hi.
Some of you may have noticed that I implemented static variables with function scope a day or two ago. Example:
int inc() { static int counter; return counter++; }
The problem in Pike is that the intended lifetime of the variable isn't always obvious; consider nested classes:
class X { int inc() { static int counter; return counter++; } }
int main() { function(:int) X1 = X()->inc; function(:int) X2 = X()->inc; write("X1:%O,%O\n" "X2:%O\n", X1(), X1(), X2()); }
Should the above output X1:0,1 X2:0, or X1:0,1 X2:2? ie should the variable be stored in X or in the top-level class?
Pike also has nested functions; consider:
function(:int) X() { int inc() { static int counter; return counter++; }; return inc; }
int main() { function(:int) X1 = X(); function(:int) X2 = X(); write("X1:%O,%O\n" "X2:%O\n", X1(), X1(), X2()); }
Same question.
I believe that the most useful would be to store the variable in the closest surrounding scope (ie X1:0,1 X2:0 for both of the above), and then have some extra syntax for specifying some other scope. The question is what the syntax should be?
Any suggestions?
I think the scope of the variable lifetime should be the object the function is in.
Ie,
class Foo { int foo(int a) { function f=lambda() { static int x=0; x++; return x+a; }; return f(); } }
the variable x should still be stored in the object defined by the class.
It should work well defining the scope with a parenthesis, like
static(object) // closest object parent static(global) // totally global, same for each instance of the object static(parent) // closest parent static(<name>) static(foo) // stored with parent function above static(Foo) // stored with parent object above
It should work well defining the scope with a parenthesis, like
static(object) // closest object parent static(global) // totally global, same for each instance of the object static(parent) // closest parent static(<name>) static(foo) // stored with parent function above static(Foo) // stored with parent object above
+1, but with minor reservation for "parent", which would clash with an outer function or class somewhere named "parent", overshadowing either of the two scopes (the parent scope / the named scope).
("object" will of course do, as it's already a reserved keyword, and global, while not a keyword IIRC, is already used as a reference to the global scope, if not overridden, so that usage already has some precedence to it.)
Yes, I'm open for the actual naming of the keywords. Preferably they should be already existing ones, so there is no name clash.
The problem in Pike is that the intended lifetime of the variable isn't always obvious; consider nested classes:
class X { int inc() { static int counter; return counter++; } }
int main() { function(:int) X1 = X()->inc; function(:int) X2 = X()->inc; write("X1:%O,%O\n" "X2:%O\n", X1(), X1(), X2()); }
Should the above output X1:0,1 X2:0, or X1:0,1 X2:2? ie should the variable be stored in X or in the top-level class?
My intuition says storage should be where it was declared, i e X1:0,1 X2:0 with the above example. Given the proposed semantics, for:
class X { static int counter; int inc() { return counter++; } }
...I would consider X1:0,1 X2:2 be the correct corresponding output. You don't state whether this case, where the "static" modifier already has semantics tradition in Pike, will do the same thing, though. Would and should it?
Pike also has nested functions; consider:
function(:int) X() { int inc() { static int counter; return counter++; }; return inc; }
int main() { function(:int) X1 = X(); function(:int) X2 = X(); write("X1:%O,%O\n" "X2:%O\n", X1(), X1(), X2()); }
Same question.
I'm less opinioned here; I would not mind X1:0,1 X2:2 in this case, or the first option, as long as which result you'd get is guaranteed.
I believe that the most useful would be to store the variable in the closest surrounding scope (ie X1:0,1 X2:0 for both of the above), and then have some extra syntax for specifying some other scope.
I think the most useful is the most readable (least susceptible to misunderstanding, when the code is read by humans). Which one that is, I don't know; I'm just one data point there. I can understand and to some extent appreciate that the proposal above would be the easiest to implement / maintain, if that is what you meant, but since you suggest supporting both, it probably wasn't. :-)
On Mon, 17 Sep 2007 10:25:01 +0200, Henrik GrubbstrXm (Lysator) @ Pike (-) developers forum 10353@lyskom.lysator.liu.se wrote:
Hi.
Some of you may have noticed that I implemented static variables with function scope a day or two ago. Example:
int inc() { static int counter; return counter++; }
The problem in Pike is that the intended lifetime of the variable isn't always obvious; consider nested classes:
class X { int inc() { static int counter; return counter++; } }
int main() { function(:int) X1 = X()->inc; function(:int) X2 = X()->inc; write("X1:%O,%O\n" "X2:%O\n", X1(), X1(), X2()); }
Should the above output X1:0,1 X2:0, or X1:0,1 X2:2? ie should the variable be stored in X or in the top-level class?
As I perceive it from other languages, 'static' means a really global (non object-oriented) memory location. Whenever int() is called from anywhere, be it different objects or functions, I would expect 'counter' to be shared. So, I would vote for the 'X1:0,1 X2:2' solution.
Bernd
As I perceive it from other languages, 'static' means a really global (non object-oriented) memory location.
I think this sentence is the perfect example of why "static(global)" (assuming we end up with Mirar's suggested syntax for explicit storage location declarations) should have to be declared using the verbose syntax, rather than be the default behaviour of the static keyword.
On Mon, 17 Sep 2007 12:45:00 +0200, Johan SundstrXm (Achtung Liebe!) @ Pike (-) developers forum 10353@lyskom.lysator.liu.se wrote:
As I perceive it from other languages, 'static' means a really global (non object-oriented) memory location.
I think this sentence is the perfect example of why "static(global)" (assuming we end up with Mirar's suggested syntax for explicit storage location declarations) should have to be declared using the verbose syntax, rather than be the default behaviour of the static keyword.
Sorry if I play the "spoilsport", but do we really need this feature at all? We can always store global values in global variables. As I see it, the number of uses are reasonably few.
The Pike-Language is much too complex anyway, with 1001 possibilities to express the same thing. Perhaps it is just the influence of Niklaus Wirth here in Zurich ;-) ... I just have the feeling that it gets more and more difficult for me to grasp the full Pike language, which gets us closer to C++ with every little step.
Bernd
PS: I do not want to kill it off, all I want to do is to ask for the "Why" ... I had been envolved in the ECMA Eiffel standardization process and I could see too many "nice to have" features slip into the language. :-( So, having a jester asking why could clarify things ... ;-)
It's both a very good and valid question. I personally rarely want it, but have occasionally used (fairly hideous) kludges to bypass the lack of it.
This pretty much sums up my position as well. If you want something to have a lifetime associated with a surrounding scope, just declare it in that scope. Problem solved.
Putting it in the top scope of your compilation unit is a simple hack, for the many basic cases where that happens to be the global scope.
Another way is to add_constant() it in a separate loader file which compiles and spawns your main code.
A third variant I just tried failed on a bus error in 7.6 and 7.7: global.hello = "world!";
Putting it in the top scope of your compilation unit is a simple hack, for the many basic cases where that happens to be the global scope.
That's not entirely global though. That's an object scope. Just because you're unlikely to create more instances of that object doesn't make it global. :)
Another way is to add_constant() it in a separate loader file which compiles and spawns your main code.
Which doesn't give a variable. You can create a sort of global scope with it though, add_constant("my_globals",([])) or so. But it needs to be done before you compile anything that wants to use it.
A third variant I just tried failed on a bus error in 7.6 and 7.7: global.hello = "world!";
Interesting.
That's not entirely global though. That's an object scope. Just because you're unlikely to create more instances of that object doesn't make it global. :)
That's as global you get in pike. Even the master is just an object. Let's keep it that way. If you want just one instance of something, putting it in a singleton object (like a pmod for example) solves the problem.
I agree here that having a static that means "moved out a scope" is something that adds very little value. Basically you only use it to hide the accessibility of the static declared variable to the context where it was declared compared to if you declared it in the outter context. There are far worse problems in Pike when it comes to information hiding than this one.
One use would be this:
class MyClass { static(global) instances=0; int my_instance_number=instances++;
... }
As far as I'm concerned, if someone needs a functionality in Pike, feel free to add it. You don't need to use all functionality to be able to use Pike.
Just make sure that it works.
what's the (global) modifier adding that is not already clear without it?
greetings, martin.
This:
class TopClass { class MyClass { static(global) instances=0; int my_instance_number=instances++;
... }
object the() { return MyClass(); } }
TopClass top1=TopClass(); TopClass top2=TopClass();
top1->the()->my_instance_number == 0 top2->the()->my_instance_number == 1 top1->the()->my_instance_number == 2
without a top-global counter, you would get
top1->the()->my_instance_number == 0 top2->the()->my_instance_number == 0 top1->the()->my_instance_number == 1
which sometimes is something that you *want* as well.
I like static. It's a namespace saver. I don't think it's confusing at all, and it has it's uses. Just like ?: and ,.
if you want a top-global clunter you could do: it's not like you are going to have lots of inner classes, and being in a class the whole thing is already reasonably self contained.
class TopClass { static myclass_instances=0; class MyClass { int my_instance_number=myclass_instances++;
... }
object the() { return MyClass(); } }
this still needs static, and i am not arguing against static, i just prefer it to be simple and not made to complex.
static should help solve the problems that can't be solved otherwise
greetings, martin.
If I do that, I might just as well do
// Make this function as local as you wish (even lambda). mapping get_my_globals() { mapping m=master()->resolv("Mirar's globals"); if (m) return m; m=(["myclass_instances":0]); add_constant("Mirar's globals",m); return m; }
class TopClass { class MyClass { int my_instance_number=get_my_globals()->myclass_instances++;
... }
object the() { return MyClass(); } }
if I'm going to clutter namespaces outside the interesting scope, anyway. And I can do that without static. I like static because it solves the problem in a /nice/ way, so lets leave it open to solve all instances of the same problem?
i see a difference in those two:
in an environment where you have code from different sources (like roxen modules) master()->resolv("Mirar's globals"); would be something that is accessible to any module (ie truly global) that is often something that should be avoided.
however when working with nested classes, you already get a very selfcontained outer class. and while i agree that it may clutter the namespace of the outer class, it does not produce a situation where the variable is accessible by just anybody, because the classes are selfcontained.
it is quite a difference having to put a variable in the right scope to make it work, vs having to write a chunk of code and void any protection or write more code to get that protection.
greetings, martin.
But the scope of the variable is local. The storage of the variable isn't. That's when you want to use static. How non-local the storage is isn't that easy to predict, unless you lock it on something (say the first real object parent).
If you have functions created on the fly (lambda or local functions) that contain a static variable, for instance.
Just like breaks, you get to a point where you rather be able to label what it should do.
It's easier to read what it does when it does it in the scope it does it, than chasing what happens in two or more directions.
Sorry if I play the "spoilsport", but do we really need this feature at all? We can always store global values in global variables. As I see it, the number of uses are reasonably few.
The same could be said for C, but there the use of include files leads to potential name-collisions at the top-level.
The main potential use I see in Pike, is where static storage is needed for some macro, but this could be solved by splitting out the storage declaration into a separate macro.
Bernd
Anyway, the consensus seems to be "Why?", and I more or less agree. The reason I implemented it was that it was on an old wishlist, and it was reasonably simple to implement.
I've now reverted the change.
I did too (specifically, i wished for a 'global' modifier). :-)
The reson being things like the above mentioned id:s, and code like this:
DiskChunk.pike // Let's say that we have 1000000 of these.. | | int hash; | DataBase parent; // 12Mb RAM goes *poof* here. :-) | | void create( DataBase par ) { parent = par; } | // .. code using variables above ..
compared to
DiskChunk.pike | | int hash; | global DataBase parent; // 12bytes RAM here, instead, gives 50% in-ram size. :-) | | void create( DataBase par ) { parent = par; } | // .. code using variables above ..
The alternative is to create a Global.pmod or similar, that contains a function that returns the parent object (if it is to be a constant, it has to be known compile time, so in practice a function is almost always needed)
I did too (specifically, i wished for a 'global' modifier). :-)
The reson being things like the above mentioned id:s, and code like this:
[...]
An alternative would be
DiskStuff.pmod | | DataBase parent; | class DiskChunk | { | int hash | void create(DataBase par) { parent = par; } | // .. code using variables above .. | }
and then use DiskStuff.DiskChunk rather than DiskChunk.
Personally, I'd like to see things implemented as in java, where you can define members of a class as (Java static), and they become "singletons" in that class, accessible without instantiating the class.
That, I imagine isn't a simple thing to permit (though, I've noticed that it's theoretically possible on the C-leve)l.
DiskChunk.pike:
something-not-static Database parent;
// can be called from the class directly. something-not-static set_parent(Database parent) { parent = this->parent; }
void create() { }
On Sep 18, 2007, at 11:25 AM, Per Hedbor () @ Pike (-) developers forum wrote:
I did too (specifically, i wished for a 'global' modifier). :-)
The reson being things like the above mentioned id:s, and code like this:
DiskChunk.pike // Let's say that we have 1000000 of these.. | | int hash; | DataBase parent; // 12Mb RAM goes *poof* here. :-) | | void create( DataBase par ) { parent = par; } | // .. code using variables above ..
On Tue, Sep 18, 2007 at 02:00:01PM +0000, Henrik Grubbstr�m (Lysator) @ Pike (-) developers forum wrote:
Anyway, the consensus seems to be "Why?"
is that really the case?
i thought the consensus from the last conference was more like that it is waiting for someone to be implemented.
the 'why' in this discussion seems to be more about the particular implementation.
static variables on the class level are useful, because the class level is the top scope. you can't go higher than that, (except if you are nesting classes, which i personally am not very fond of) also classes should be self contained, so having a variable living outside a class that is needed inside is not very nice.
should we do a show of hands?
greetings, martin.
On Tue, 18 Sep 2007 16:47:04 +0200, Martin Bähr mbaehr@email.archlab.tuwien.ac.at wrote:
On Tue, Sep 18, 2007 at 02:00:01PM +0000, Henrik Grubbstr�m (Lysator) @ Pike (-) developers forum wrote:
Anyway, the consensus seems to be "Why?"
is that really the case?
i thought the consensus from the last conference was more like that it is waiting for someone to be implemented.
I was not participating in the last conference, so I am perhaps not the best to raise the question "why" if it was already discussed there. It is probably more a cleanup of the language, and less a matter of complicating the language.
In the end, I should just attend the next Pike conference :-)
Bernd
On Tue, Sep 18, 2007 at 05:06:46PM +0200, Bernd Schoeller wrote:
I was not participating in the last conference, so I am perhaps not the best to raise the question "why" if it was already discussed there.
participating in the conference is not a requirement to participate in discussion. i don't remember any discussion or voices against static from the conference, so probably until now the question of 'why' has actually never been raised.
and even if it has, then we should be able to provide the answer...
It is probably more a cleanup of the language, and less a matter of complicating the language.
that's the direction i would like to see it go.
In the end, I should just attend the next Pike conference :-)
that indeed you should :-)
greetings, martin.
On Tue, Sep 18, 2007 at 02:00:01PM +0000, Henrik Grubbstr�m (Lysator) @ Pike (-) developers forum wrote:
Anyway, the consensus seems to be "Why?"
is that really the case?
The traffic here and off-list would suggest so.
the 'why' in this discussion seems to be more about the particular implementation.
Not really. Why we need it a really good question, and no-one I asked could come up with any good use for it. That's partly because I don't consider grubbas macro example _good_ use, but still.
On Mon, Sep 17, 2007 at 08:25:01AM +0000, Henrik Grubbstr�m (Lysator) @ Pike (-) developers forum wrote:
function(:int) X1 = X()->inc; function(:int) X2 = X()->inc;
my gut feeling tells me that i'd expect two counters here. these two inc functions are not the same.
it would be different if the function itself were declared static: class X { static int inc() { static int counter; return counter++; } }
now i'd expect inc() to be shared accross all instances of X
I believe that the most useful would be to store the variable in the closest surrounding scope (ie X1:0,1 X2:0 for both of the above), and then have some extra syntax for specifying some other scope. The question is what the syntax should be?
is that syntax needed? wouldn't that already be solved if you declare the variable in the scope where you want it?
btw, what is the difference between a static variable in a function
class X { int inc() { static int counter; return counter++; } }
and just using a variable in the class scope or a closure?
class X { int counter; int inc() { return counter++; } }
both give the same result with:
int main() { function(:int) X1 = X()->inc; function(:int) X2 = X()->inc; write("X1:%O,%O\n" "X2:%O\n", X1(), X1(), X2()); }
so having static inside a function is really just a disguised object variable. except that other functions can't reach that variable, so there may be some sense to that, but in general i think static variables on the class level are more interesting.
greetings, martin.
pike-devel@lists.lysator.liu.se