Grubba and I started talking a bit about adding lvalues as an explicit data type in Pike. I'll jot down what we arrived at for the record (well, some of it are later thoughts by me):
An lvalue is a pointer to a variable that allows the variable to be changed (as opposed to a pointer to a function). Generic variable pointers could be implemented by adding two new svalue types: Pointer to variable in object and pointer to variable in frame (if programs get "proper" static variables in some future they would get this too).
The frame case is used for pointers to local variables in functions. Refcounting in frames is already implemented for the sake of nested functions, so returning a pointer to a local variable shouldn't be a problem.
The solution with special pointer svalue types is better than the double svalue (the thing to index followed by the index) approach currently used internally for lvalues for two reasons: Firstly the index operation is performed when the pointer is created instead of when it's dereferenced, which trigs errors closer to the source. Secondly it would avoid odd effects in various functions that doesn't expect variable pointers.
Pointers to values in arrays, mappings and multisets don't need new svalue types since their iterator objects can be used then (more on that later).
Variable pointers would be used like in C. Silly example:
int append_to_buf (Stdio.File f, pointer(string) buf) { string in = f->read(); *buf += in; return sizeof (in); }
Some new operator lfuns would be needed:
&foo => Get the pointer to foo; calls `& in foo. &foo->bar => Get the pointer to foo->bar; calls `&->("bar") in foo. &foo[bar] => Ditto for []; calls `&[](bar) in foo.
`& and ``& already exist, so the first operator above would actually become ```& with the current naming system. We're however contemplating a new system with more verbose lfun names; `&this for the new operator, `this&_ for `&, and `_&this for ``&.
The similarity between pointers and iterators is obvious, so iterators should be able to act as pointers. To accomplish that an lfun corresponding to the dereferencing * operator is necessary:
*foo => Gets the value the pointer points to; calls `*this in foo.
It's also interesting to have an lfun that returns a pointer svalue so that it can be used in an lvalue context. An example:
for (pointer ptr = &my_obj[0]; *ptr; ptr++) *ptr = reverse (*ptr); ^^^^ Here `&*this would be called to produce a pointer value which is used as lvalue.
If `&*this returns an object instead of a basic pointer svalue, `&*this would presumably be tried again, which would make it possible to produce a infinite loop if `&*this should be silly enough to return the iterator itself. A simple check for that might be a good idea.
To be symmetric with `[]= and `->=, one would add a `*this=_ instead of `&*this. I do however like `&*this better since it's then possible to do e.g.
*ptr += ({17});
with an efficient `+= operation without the need for a special `*this+=_.
A `&*this lfun makes it possible to try it in any expression where no better suited lfun is available:
foo + bar => Try: foo->`+(bar) Then: bar->``+(foo) Then: *(foo->`&*this) + bar Then: foo + *(bar->`&*this)
That could be handy when implementing e.g. "smart" strings like Locale.DeferredLocale. I don't know yet if it overall is a good idea or not, though. It's clearly not a good idea in simple assignments and arbitrary function calls.
Variable pointers would be used like in C. Silly example:
Since you talked about an lvalue type, which is what sscanf currently uses, I would have expected a more C++-ish usage, i.e.
int append_to_buf (Stdio.File f, string &buf) { string in = f->read(); buf += in; return sizeof (in); }
... append_to_buf(f, buf); ...
After all, you don't do `sscanf(bla, "%d", &n)' in Pike.
/ Marcus Comstedt (ACROSS) (Hail Ilpalazzo!)
Previous text:
2003-04-16 00:55: Subject: Pointers/lvalues
Grubba and I started talking a bit about adding lvalues as an explicit data type in Pike. I'll jot down what we arrived at for the record (well, some of it are later thoughts by me):
An lvalue is a pointer to a variable that allows the variable to be changed (as opposed to a pointer to a function). Generic variable pointers could be implemented by adding two new svalue types: Pointer to variable in object and pointer to variable in frame (if programs get "proper" static variables in some future they would get this too).
The frame case is used for pointers to local variables in functions. Refcounting in frames is already implemented for the sake of nested functions, so returning a pointer to a local variable shouldn't be a problem.
The solution with special pointer svalue types is better than the double svalue (the thing to index followed by the index) approach currently used internally for lvalues for two reasons: Firstly the index operation is performed when the pointer is created instead of when it's dereferenced, which trigs errors closer to the source. Secondly it would avoid odd effects in various functions that doesn't expect variable pointers.
Pointers to values in arrays, mappings and multisets don't need new svalue types since their iterator objects can be used then (more on that later).
Variable pointers would be used like in C. Silly example:
int append_to_buf (Stdio.File f, pointer(string) buf) { string in = f->read(); *buf += in; return sizeof (in); }
Some new operator lfuns would be needed:
&foo => Get the pointer to foo; calls `& in foo. &foo->bar => Get the pointer to foo->bar; calls `&->("bar") in foo. &foo[bar] => Ditto for []; calls `&[](bar) in foo.
`& and ``& already exist, so the first operator above would actually become ```& with the current naming system. We're however contemplating a new system with more verbose lfun names; `&this for the new operator, `this&_ for `&, and `_&this for ``&.
The similarity between pointers and iterators is obvious, so iterators should be able to act as pointers. To accomplish that an lfun corresponding to the dereferencing * operator is necessary:
*foo => Gets the value the pointer points to; calls `*this in foo.
It's also interesting to have an lfun that returns a pointer svalue so that it can be used in an lvalue context. An example:
for (pointer ptr = &my_obj[0]; *ptr; ptr++) *ptr = reverse (*ptr); ^^^^ Here `&*this would be called to produce a pointer value which is used as lvalue.
If `&*this returns an object instead of a basic pointer svalue, `&*this would presumably be tried again, which would make it possible to produce a infinite loop if `&*this should be silly enough to return the iterator itself. A simple check for that might be a good idea.
To be symmetric with `[]= and `->=, one would add a `*this=_ instead of `&*this. I do however like `&*this better since it's then possible to do e.g.
*ptr += ({17});
with an efficient `+= operation without the need for a special `*this+=_.
A `&*this lfun makes it possible to try it in any expression where no better suited lfun is available:
foo + bar => Try: foo->`+(bar) Then: bar->``+(foo) Then: *(foo->`&*this) + bar Then: foo + *(bar->`&*this)
That could be handy when implementing e.g. "smart" strings like Locale.DeferredLocale. I don't know yet if it overall is a good idea or not, though. It's clearly not a good idea in simple assignments and arbitrary function calls.
/ Martin Stjernholm, Roxen IS
I agree with this sentiment (as obvious from my own post). Since the language has no dynamically allocated memory (in the "malloc" sense), I don't think it makes sense to use _pointers_, when references would be clearer. The fact that sscanf already does this is a good reason to make it an official implementation (that also fixes the problem some people have with 'sscanf' not being a valid Pike method, since it then could be :-).
/ David Hedbor
Previous text:
2003-04-16 01:00: Subject: Pointers/lvalues
Variable pointers would be used like in C. Silly example:
Since you talked about an lvalue type, which is what sscanf currently uses, I would have expected a more C++-ish usage, i.e.
int append_to_buf (Stdio.File f, string &buf) { string in = f->read(); buf += in; return sizeof (in); }
... append_to_buf(f, buf); ...
After all, you don't do `sscanf(bla, "%d", &n)' in Pike.
/ Marcus Comstedt (ACROSS) (Hail Ilpalazzo!)
Yes, Grubba and I discussed that a bit too. The C-like syntax has some advantages:
o It's clear that a nonlocal value is referenced/changed in the expression where it occurs.
o Pointers are more flexible, e.g. in the use as iterators. If C++-like references are implemented there'd still be a use for pointers.
o One big feature with C++ references is that the compiler guarantees that they point to a valid location. Pike can't make a similar guarantee since it's weakly typed. So in an expression
x = 17
one could still get a runtime error like "dereferencing the NULL value".
o Implementing references would mean to fix implicit casting from pointer svalues and iterators to the values they reference. That means adding code to almost every C-level function to make such conversions on its arguments before typechecking them. In cmods it can be automated, but it'd still be a major hassle and compatibility issue.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 01:00: Subject: Pointers/lvalues
Variable pointers would be used like in C. Silly example:
Since you talked about an lvalue type, which is what sscanf currently uses, I would have expected a more C++-ish usage, i.e.
int append_to_buf (Stdio.File f, string &buf) { string in = f->read(); buf += in; return sizeof (in); }
... append_to_buf(f, buf); ...
After all, you don't do `sscanf(bla, "%d", &n)' in Pike.
/ Marcus Comstedt (ACROSS) (Hail Ilpalazzo!)
o Pointers are more flexible,
This is what scares me a bit. I'm not convinced this flexibility is benign.
/ Marcus Comstedt (ACROSS) (Hail Ilpalazzo!)
Previous text:
2003-04-16 01:16: Subject: Pointers/lvalues
Yes, Grubba and I discussed that a bit too. The C-like syntax has some advantages:
o It's clear that a nonlocal value is referenced/changed in the expression where it occurs.
o Pointers are more flexible, e.g. in the use as iterators. If C++-like references are implemented there'd still be a use for pointers.
o One big feature with C++ references is that the compiler guarantees that they point to a valid location. Pike can't make a similar guarantee since it's weakly typed. So in an expression
x = 17
one could still get a runtime error like "dereferencing the NULL value".
o Implementing references would mean to fix implicit casting from pointer svalues and iterators to the values they reference. That means adding code to almost every C-level function to make such conversions on its arguments before typechecking them. In cmods it can be automated, but it'd still be a major hassle and compatibility issue.
/ Martin Stjernholm, Roxen IS
Well, the most severe hazards with C pointers wouldn't exist anyway, such as indexing out of bounds and casting between pointer types. What other issues can there be?
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 01:23: Subject: Pointers/lvalues
o Pointers are more flexible,
This is what scares me a bit. I'm not convinced this flexibility is benign.
/ Marcus Comstedt (ACROSS) (Hail Ilpalazzo!)
Well, how about this:
void foo(pointer(int) x) { *--x = 7; }
void bar() { array(int) q = ({ 47, 11 }); foo(&q[1]); }
I think this is slightly disturbing. bar has not shared a reference to q[0] with anybody, and yet the value of q[0] gets changed under its feet.
/ Marcus Comstedt (ACROSS) (Hail Ilpalazzo!)
Previous text:
2003-04-16 01:31: Subject: Pointers/lvalues
Well, the most severe hazards with C pointers wouldn't exist anyway, such as indexing out of bounds and casting between pointer types. What other issues can there be?
/ Martin Stjernholm, Roxen IS
Yes, I agree that's slightly disturbing, but I'm not sure I find it disturbing enough to matter. Anyway, it can be avoided by using restricted pointer values instead of iterators.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 01:53: Subject: Pointers/lvalues
Well, how about this:
void foo(pointer(int) x) { *--x = 7; }
void bar() { array(int) q = ({ 47, 11 }); foo(&q[1]); }
I think this is slightly disturbing. bar has not shared a reference to q[0] with anybody, and yet the value of q[0] gets changed under its feet.
/ Marcus Comstedt (ACROSS) (Hail Ilpalazzo!)
/.../ If C++-like references are implemented there'd still be a use for pointers.
That wasn't really true. C++-like references together with the current iterators would pretty much make up for pointers.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 01:16: Subject: Pointers/lvalues
Yes, Grubba and I discussed that a bit too. The C-like syntax has some advantages:
o It's clear that a nonlocal value is referenced/changed in the expression where it occurs.
o Pointers are more flexible, e.g. in the use as iterators. If C++-like references are implemented there'd still be a use for pointers.
o One big feature with C++ references is that the compiler guarantees that they point to a valid location. Pike can't make a similar guarantee since it's weakly typed. So in an expression
x = 17
one could still get a runtime error like "dereferencing the NULL value".
o Implementing references would mean to fix implicit casting from pointer svalues and iterators to the values they reference. That means adding code to almost every C-level function to make such conversions on its arguments before typechecking them. In cmods it can be automated, but it'd still be a major hassle and compatibility issue.
/ Martin Stjernholm, Roxen IS
Without arguing the case whether pike should get official pointers (as opposed to reference types that kind-of become like pointers), why use pointer(string)? What's wrong with
void method(string &var) {}
? It'd be using the C++ reference syntax rather. Even traditional C-syntax would work:
void method(string *var) {}
The use of type* as an alias for array(type) has been deprecated long enough to be repurposed (with the possible problem that backwards compatibility might become tricky).
Overall, do we need "pointer" syntax or is it enough to be able to pass all variables by reference? By reference I mean the C++ "reference" idea, such as:
void getXY(int pos, int &x, int& y) { x = (pos>>8)&0xff; y = pos&0xff; }
... int my_x, my_y; getXY(my_pos, my_x, my_y);
The benefit is that you don't need to mix in pointer syntax and all the issues related to that. The drawback is that it's less clear from the calling method point of view. Other drawbacks in terms of the implementaiton I won't comment on since I'm not qualified to do so.
/ David Hedbor
Previous text:
2003-04-16 00:55: Subject: Pointers/lvalues
Grubba and I started talking a bit about adding lvalues as an explicit data type in Pike. I'll jot down what we arrived at for the record (well, some of it are later thoughts by me):
An lvalue is a pointer to a variable that allows the variable to be changed (as opposed to a pointer to a function). Generic variable pointers could be implemented by adding two new svalue types: Pointer to variable in object and pointer to variable in frame (if programs get "proper" static variables in some future they would get this too).
The frame case is used for pointers to local variables in functions. Refcounting in frames is already implemented for the sake of nested functions, so returning a pointer to a local variable shouldn't be a problem.
The solution with special pointer svalue types is better than the double svalue (the thing to index followed by the index) approach currently used internally for lvalues for two reasons: Firstly the index operation is performed when the pointer is created instead of when it's dereferenced, which trigs errors closer to the source. Secondly it would avoid odd effects in various functions that doesn't expect variable pointers.
Pointers to values in arrays, mappings and multisets don't need new svalue types since their iterator objects can be used then (more on that later).
Variable pointers would be used like in C. Silly example:
int append_to_buf (Stdio.File f, pointer(string) buf) { string in = f->read(); *buf += in; return sizeof (in); }
Some new operator lfuns would be needed:
&foo => Get the pointer to foo; calls `& in foo. &foo->bar => Get the pointer to foo->bar; calls `&->("bar") in foo. &foo[bar] => Ditto for []; calls `&[](bar) in foo.
`& and ``& already exist, so the first operator above would actually become ```& with the current naming system. We're however contemplating a new system with more verbose lfun names; `&this for the new operator, `this&_ for `&, and `_&this for ``&.
The similarity between pointers and iterators is obvious, so iterators should be able to act as pointers. To accomplish that an lfun corresponding to the dereferencing * operator is necessary:
*foo => Gets the value the pointer points to; calls `*this in foo.
It's also interesting to have an lfun that returns a pointer svalue so that it can be used in an lvalue context. An example:
for (pointer ptr = &my_obj[0]; *ptr; ptr++) *ptr = reverse (*ptr); ^^^^ Here `&*this would be called to produce a pointer value which is used as lvalue.
If `&*this returns an object instead of a basic pointer svalue, `&*this would presumably be tried again, which would make it possible to produce a infinite loop if `&*this should be silly enough to return the iterator itself. A simple check for that might be a good idea.
To be symmetric with `[]= and `->=, one would add a `*this=_ instead of `&*this. I do however like `&*this better since it's then possible to do e.g.
*ptr += ({17});
with an efficient `+= operation without the need for a special `*this+=_.
A `&*this lfun makes it possible to try it in any expression where no better suited lfun is available:
foo + bar => Try: foo->`+(bar) Then: bar->``+(foo) Then: *(foo->`&*this) + bar Then: foo + *(bar->`&*this)
That could be handy when implementing e.g. "smart" strings like Locale.DeferredLocale. I don't know yet if it overall is a good idea or not, though. It's clearly not a good idea in simple assignments and arbitrary function calls.
/ Martin Stjernholm, Roxen IS
What's wrong with
void method(string &var) {}
?
It looks like the variable &var is declared as a string. I'd prefer "reference(string) var" or "ref(string) var". It impacts less on all the parsers that has to be updated as well.
/ Martin Nilsson (har bott i google)
Previous text:
2003-04-16 01:04: Subject: Pointers/lvalues
Without arguing the case whether pike should get official pointers (as opposed to reference types that kind-of become like pointers), why use pointer(string)? What's wrong with
void method(string &var) {}
? It'd be using the C++ reference syntax rather. Even traditional C-syntax would work:
void method(string *var) {}
The use of type* as an alias for array(type) has been deprecated long enough to be repurposed (with the possible problem that backwards compatibility might become tricky).
Overall, do we need "pointer" syntax or is it enough to be able to pass all variables by reference? By reference I mean the C++ "reference" idea, such as:
void getXY(int pos, int &x, int& y) { x = (pos>>8)&0xff; y = pos&0xff; }
... int my_x, my_y; getXY(my_pos, my_x, my_y);
The benefit is that you don't need to mix in pointer syntax and all the issues related to that. The drawback is that it's less clear from the calling method point of view. Other drawbacks in terms of the implementaiton I won't comment on since I'm not qualified to do so.
/ David Hedbor
It IS a string, but a reference to it. Make it 'string& ref' is perhaps clearer but syntactically the same.
/ David Hedbor
Previous text:
2003-04-16 01:13: Subject: Pointers/lvalues
What's wrong with
void method(string &var) {}
?
It looks like the variable &var is declared as a string. I'd prefer "reference(string) var" or "ref(string) var". It impacts less on all the parsers that has to be updated as well.
/ Martin Nilsson (har bott i google)
What is/are the driving force/forces behind the proposal? Making pike level special forms such as sscanf possible?
/ Johan Sundström (folkskådare)
Previous text:
2003-04-16 00:55: Subject: Pointers/lvalues
Grubba and I started talking a bit about adding lvalues as an explicit data type in Pike. I'll jot down what we arrived at for the record (well, some of it are later thoughts by me):
An lvalue is a pointer to a variable that allows the variable to be changed (as opposed to a pointer to a function). Generic variable pointers could be implemented by adding two new svalue types: Pointer to variable in object and pointer to variable in frame (if programs get "proper" static variables in some future they would get this too).
The frame case is used for pointers to local variables in functions. Refcounting in frames is already implemented for the sake of nested functions, so returning a pointer to a local variable shouldn't be a problem.
The solution with special pointer svalue types is better than the double svalue (the thing to index followed by the index) approach currently used internally for lvalues for two reasons: Firstly the index operation is performed when the pointer is created instead of when it's dereferenced, which trigs errors closer to the source. Secondly it would avoid odd effects in various functions that doesn't expect variable pointers.
Pointers to values in arrays, mappings and multisets don't need new svalue types since their iterator objects can be used then (more on that later).
Variable pointers would be used like in C. Silly example:
int append_to_buf (Stdio.File f, pointer(string) buf) { string in = f->read(); *buf += in; return sizeof (in); }
Some new operator lfuns would be needed:
&foo => Get the pointer to foo; calls `& in foo. &foo->bar => Get the pointer to foo->bar; calls `&->("bar") in foo. &foo[bar] => Ditto for []; calls `&[](bar) in foo.
`& and ``& already exist, so the first operator above would actually become ```& with the current naming system. We're however contemplating a new system with more verbose lfun names; `&this for the new operator, `this&_ for `&, and `_&this for ``&.
The similarity between pointers and iterators is obvious, so iterators should be able to act as pointers. To accomplish that an lfun corresponding to the dereferencing * operator is necessary:
*foo => Gets the value the pointer points to; calls `*this in foo.
It's also interesting to have an lfun that returns a pointer svalue so that it can be used in an lvalue context. An example:
for (pointer ptr = &my_obj[0]; *ptr; ptr++) *ptr = reverse (*ptr); ^^^^ Here `&*this would be called to produce a pointer value which is used as lvalue.
If `&*this returns an object instead of a basic pointer svalue, `&*this would presumably be tried again, which would make it possible to produce a infinite loop if `&*this should be silly enough to return the iterator itself. A simple check for that might be a good idea.
To be symmetric with `[]= and `->=, one would add a `*this=_ instead of `&*this. I do however like `&*this better since it's then possible to do e.g.
*ptr += ({17});
with an efficient `+= operation without the need for a special `*this+=_.
A `&*this lfun makes it possible to try it in any expression where no better suited lfun is available:
foo + bar => Try: foo->`+(bar) Then: bar->``+(foo) Then: *(foo->`&*this) + bar Then: foo + *(bar->`&*this)
That could be handy when implementing e.g. "smart" strings like Locale.DeferredLocale. I don't know yet if it overall is a good idea or not, though. It's clearly not a good idea in simple assignments and arbitrary function calls.
/ Martin Stjernholm, Roxen IS
Yes, or in more general terms make it easier to use call-by-reference. I also like the concept of viewing iterators as "intelligent pointers", and the `&*this lfun can prove useful in various circumstances.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 01:16: Subject: Pointers/lvalues
What is/are the driving force/forces behind the proposal? Making pike level special forms such as sscanf possible?
/ Johan Sundström (folkskådare)
I am this sceptical: |------------------------------|
/ Martin Nilsson (har bott i google)
Previous text:
2003-04-16 01:28: Subject: Pointers/lvalues
Yes, or in more general terms make it easier to use call-by-reference. I also like the concept of viewing iterators as "intelligent pointers", and the `&*this lfun can prove useful in various circumstances.
/ Martin Stjernholm, Roxen IS
I can't think of much use for pointers. From the calling side I have not had any troubles using objects. Am I totally lost if I say that a `= lfun would solve most of the problems on the calling side of the function?
As for the called side, isn't pointers just convenience suger for APIs? #define sscanf(X,Y,Z ...) [Z]=array_sscanf(X,Y)
/ Martin Nilsson (har bott i google)
Previous text:
2003-04-16 01:35: Subject: Pointers/lvalues
I value unfounded arguments this much: ||
/ Martin Stjernholm, Roxen IS
I don't know how you intend to use such a `= lfun, or indeed how it would work at all. Can you elaborate?
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 01:46: Subject: Pointers/lvalues
I can't think of much use for pointers. From the calling side I have not had any troubles using objects. Am I totally lost if I say that a `= lfun would solve most of the problems on the calling side of the function?
As for the called side, isn't pointers just convenience suger for APIs? #define sscanf(X,Y,Z ...) [Z]=array_sscanf(X,Y)
/ Martin Nilsson (har bott i google)
My idea is that if an object has a `= lfun it will be called with the assignment value for the variable and gets the actual value to assign in return. Creating a secure string variable could be based on code along the following lines:
class HiddenString { string hidden; this_program `=(mixed x) { if(stringp(x)) { hidden=x; return this_object(); } return x; } }
/ Martin Nilsson (har bott i google)
Previous text:
2003-04-16 01:54: Subject: Pointers/lvalues
I don't know how you intend to use such a `= lfun, or indeed how it would work at all. Can you elaborate?
/ Martin Stjernholm, Roxen IS
If I understand you correctly, your `= lfun would imply that an assignment to a variable becomes dependent on the old value in it. That's not something I'd expect of an assignment; in my view an assignment operates on a variable, not the value in it. It'd make objects too slippery for my taste:
object x = a_fishy_object; ... x = a_nice_object; if (x->is_fishy_object) { werror ("Foo?\n"); x = 0; // Begone! if (objectp (x) && x->is_fishy_object) { werror ("Arrgh!\n"); destruct (x); // Die, sucker! } }
If lfuns could be added to variables then a `= would make sense.
Anyway, how would such a thing help simulating pass-by-reference on the calling side? Where would the object instance with the `= come from?
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 02:02: Subject: Pointers/lvalues
My idea is that if an object has a `= lfun it will be called with the assignment value for the variable and gets the actual value to assign in return. Creating a secure string variable could be based on code along the following lines:
class HiddenString { string hidden; this_program `=(mixed x) { if(stringp(x)) { hidden=x; return this_object(); } return x; } }
/ Martin Nilsson (har bott i google)
Perhaps I've been a bit singleminded about "smart" strings, presented in the original text. As I understood it you would send in a reference to SmartObject->string_variable whereas I suggested to make the entire SmartObject even "smarter" and send it in instead. As for the fishyness of `= I agree that it makes things to strange to be comfortable.
The reason why I've focused on this practical problem is because it is one of the few that has been presented as a reason for introducing pointers. Obviously there has to be something good about it since most C-programmers find it a good idea, I just awaits a nice example. The most positive thing so far is that if the new lfuns are added there has to be an updated manual, because there is no way that people are going to figure out what `_&this means on their own. This paragraph sounds moodier than I intended, but you can pretend that it is more gentle and nice instead of having me rewrite it.
/ Martin Nilsson (har bott i google)
Previous text:
2003-04-16 02:26: Subject: Pointers/lvalues
If I understand you correctly, your `= lfun would imply that an assignment to a variable becomes dependent on the old value in it. That's not something I'd expect of an assignment; in my view an assignment operates on a variable, not the value in it. It'd make objects too slippery for my taste:
object x = a_fishy_object; ... x = a_nice_object; if (x->is_fishy_object) { werror ("Foo?\n"); x = 0; // Begone! if (objectp (x) && x->is_fishy_object) { werror ("Arrgh!\n"); destruct (x); // Die, sucker! } }
If lfuns could be added to variables then a `= would make sense.
Anyway, how would such a thing help simulating pass-by-reference on the calling side? Where would the object instance with the `= come from?
/ Martin Stjernholm, Roxen IS
On Wed, Apr 16, 2003 at 03:10:01AM +0200, Martin Nilsson (har bott i google) @ Pike (-) developers forum wrote:
pointers. Obviously there has to be something good about it since most C-programmers find it a good idea,
people often find something a good idea, for the lack of a better idea. pointers may be good for c because people using c can't think if a way to change that.
however, please also do not forget that all those people that do not like pointers usually turn away from c. so the popularity of pointers among c programmers really is no good measure for them being a good idea.
on the contrary, the number of people turning away from c because of pointers would be a better measure, and introducing pointers to pike would remove one potential language for them to turn to.
greetings, martin.
It seems a major problem here is the negative connotations to the word "pointer" in this crowd. Please replace it with "ahofu" and reread the suggestion for what it actually says. The resemblance with C pointers stops at the syntax, like pretty much everything else in Pike.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 03:31: Subject: Re: Pointers/lvalues
On Wed, Apr 16, 2003 at 03:10:01AM +0200, Martin Nilsson (har bott i google) @ Pike (-) developers forum wrote:
pointers. Obviously there has to be something good about it since most C-programmers find it a good idea,
people often find something a good idea, for the lack of a better idea. pointers may be good for c because people using c can't think if a way to change that.
however, please also do not forget that all those people that do not like pointers usually turn away from c. so the popularity of pointers among c programmers really is no good measure for them being a good idea.
on the contrary, the number of people turning away from c because of pointers would be a better measure, and introducing pointers to pike would remove one potential language for them to turn to.
greetings, martin.
/ Brevbäraren
but if they are not like c pointers they should have a different name. (and possible different syntax) the negative perception against pointers does not only exist in this crowd but amongst a lot of potential pike users who will look at pike, see "pointers", and turn away before they even had a chance to learn that these entities are really different.
and since someone brought up the similarity to c/c++ exactly because of such a similarity, same syntax expects similar/same behaviour. if the behaviour is really different, the syntax should be different too. (i am not sure in how many places pike violates this idea, but i'd like to keep it from increasing)
and because of the negative perception pointers have, the differences should be stressed here.
greetings, martin.
Sure, another name for them are fine if you can come up with a better one. As for syntax, it's totally in line with Pikes design to use a syntax similar to C/C++/Java where the analogy is strong, which it is here.
If it wasn't obvious already, I might stress that this suggestion is completely safe when it comes to memory handling, so the paramount dangers of C/C++ pointers don't exist. Otherwise they behave in similar fashion.
As for users that judges a language only by seeing if a concept with a certain name exists in it, without bothering to learn how it works, then I think the problem is primarily in them and not in Pike. So let them go with the buzzword of the week. We only need to ensure that the manual properly explains that memory management in Pike is high level and that that of course includes pointers.
I think the situation is pretty similar to the word "string"; it's not possible to index outside strings in Pike and they don't leak etc, yet they still carry the same name as their dangerous low-level counterparts in C.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 13:48: Subject: Re: Pointers/lvalues
but if they are not like c pointers they should have a different name. (and possible different syntax) the negative perception against pointers does not only exist in this crowd but amongst a lot of potential pike users who will look at pike, see "pointers", and turn away before they even had a chance to learn that these entities are really different.
and since someone brought up the similarity to c/c++ exactly because of such a similarity, same syntax expects similar/same behaviour. if the behaviour is really different, the syntax should be different too. (i am not sure in how many places pike violates this idea, but i'd like to keep it from increasing)
and because of the negative perception pointers have, the differences should be stressed here.
greetings, martin.
/ Brevbäraren
On Wed, Apr 16, 2003 at 04:50:02PM +0200, Martin Stjernholm, Roxen IS @ Pike developers forum wrote:
As for users that judges a language only by seeing if a concept with a certain name exists in it, without bothering to learn how it works, then I think the problem is primarily in them and not in Pike.
but issues like these make a language harder to learn, and that is crucial in getting the language accepted.
I think the situation is pretty similar to the word "string"; it's not possible to index outside strings in Pike and they don't leak etc, yet they still carry the same name as their dangerous low-level counterparts in C.
the syntax to declare strings is very different by having a "string" type, while c does not, making the difference in stringhandling abvious.
using c syntax to define pointers would not have that effect.
greetings, martin.
For me it's rather the abundance of unwieldy LFUNs that make the proposal look somewhat scary, at least right now when the fog is starting to clear up a bit.
/ Johan Sundström (folkskådare)
Previous text:
2003-04-16 13:37: Subject: Re: Pointers/lvalues
It seems a major problem here is the negative connotations to the word "pointer" in this crowd. Please replace it with "ahofu" and reread the suggestion for what it actually says. The resemblance with C pointers stops at the syntax, like pretty much everything else in Pike.
/ Martin Stjernholm, Roxen IS
You mean the proposed more verbose lfun names? That's a separate issue; one can just as well go with ```* and `&* instead (well, the latter would require compat level stuff since two tokens `& and * can form valid pike code).
The thing with the longer lfun names was to make them more clear and at the same time allow some extensions: "this" stands for the object in which the lfun is called, and "_" for other arguments passed to it.
`! => `!this `+ => `this+_ ``+ => `_+this `-> => `this->_ `->= => `this->_=_ `() => `this() ...etc
A scheme like this would also allow separate lfuns for some operators that now are forced to share lfuns with others that aren't really the same thing:
Negation: `-this (Now calls the substraction lfun with no arguments.)
Range: `this[_.._] (Now calls `[] with two arguments, and in an expression such as x[1..] or x[..17] then the missing limits are substituted with 0 or 2147483647.)
The use of "this" and "_" was only the best we could come up with yesterday; better ideas are welcome. Anyway I think a naming scheme like the above gives more clear lfun names than the current solution with different numbers of preceding backquotes.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 13:43: Subject: Re: Pointers/lvalues
For me it's rather the abundance of unwieldy LFUNs that make the proposal look somewhat scary, at least right now when the fog is starting to clear up a bit.
/ Johan Sundström (folkskådare)
As for `&*, it would perhaps be a better idea to use _get_value_pointer or something since it doesn't really correspond to any operator(s) present in the source.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 14:04: Subject: Re: Pointers/lvalues
You mean the proposed more verbose lfun names? That's a separate issue; one can just as well go with ```* and `&* instead (well, the latter would require compat level stuff since two tokens `& and * can form valid pike code).
The thing with the longer lfun names was to make them more clear and at the same time allow some extensions: "this" stands for the object in which the lfun is called, and "_" for other arguments passed to it.
`! => `!this `+ => `this+_ ``+ => `_+this `-> => `this->_ `->= => `this->_=_ `() => `this() ...etc
A scheme like this would also allow separate lfuns for some operators that now are forced to share lfuns with others that aren't really the same thing:
Negation: `-this (Now calls the substraction lfun with no arguments.)
Range: `this[_.._] (Now calls `[] with two arguments, and in an expression such as x[1..] or x[..17] then the missing limits are substituted with 0 or 2147483647.)
The use of "this" and "_" was only the best we could come up with yesterday; better ideas are welcome. Anyway I think a naming scheme like the above gives more clear lfun names than the current solution with different numbers of preceding backquotes.
/ Martin Stjernholm, Roxen IS
Ah, I did not catch on that it was an issue of renaming current LFUNs. The way I misread it, we would end up with about twice as many LFUNs, just in order to support pointers/references.
Regardless of which way we end up following in the lvalue issue, I am very much in favour of pursuing the suggested LFUN renaming scheme, with or without better suggestions for the new names.
/ Johan Sundström (folkskådare)
Previous text:
2003-04-16 14:04: Subject: Re: Pointers/lvalues
You mean the proposed more verbose lfun names? That's a separate issue; one can just as well go with ```* and `&* instead (well, the latter would require compat level stuff since two tokens `& and * can form valid pike code).
The thing with the longer lfun names was to make them more clear and at the same time allow some extensions: "this" stands for the object in which the lfun is called, and "_" for other arguments passed to it.
`! => `!this `+ => `this+_ ``+ => `_+this `-> => `this->_ `->= => `this->_=_ `() => `this() ...etc
A scheme like this would also allow separate lfuns for some operators that now are forced to share lfuns with others that aren't really the same thing:
Negation: `-this (Now calls the substraction lfun with no arguments.)
Range: `this[_.._] (Now calls `[] with two arguments, and in an expression such as x[1..] or x[..17] then the missing limits are substituted with 0 or 2147483647.)
The use of "this" and "_" was only the best we could come up with yesterday; better ideas are welcome. Anyway I think a naming scheme like the above gives more clear lfun names than the current solution with different numbers of preceding backquotes.
/ Martin Stjernholm, Roxen IS
How about lfun as a prefix? Not only for the new ones, then, but as an alternative to ´ in general:
class Foo { Foo lfun +(int x) { // or lfun::+? }
Foo lfun ´+(int x) { // (``+), perhapos +', as in +-2. :-) }
Foo lfun m_delete(int x) { // as in _m_delete } }
Or perhaps 'operator', but that would be somewhat misleading in pike.
class Foo { Foo operator *(int x) { }
Foo operator m_delete(int x) { } }
/ Per Hedbor ()
Previous text:
2003-04-16 14:04: Subject: Re: Pointers/lvalues
You mean the proposed more verbose lfun names? That's a separate issue; one can just as well go with ```* and `&* instead (well, the latter would require compat level stuff since two tokens `& and * can form valid pike code).
The thing with the longer lfun names was to make them more clear and at the same time allow some extensions: "this" stands for the object in which the lfun is called, and "_" for other arguments passed to it.
`! => `!this `+ => `this+_ ``+ => `_+this `-> => `this->_ `->= => `this->_=_ `() => `this() ...etc
A scheme like this would also allow separate lfuns for some operators that now are forced to share lfuns with others that aren't really the same thing:
Negation: `-this (Now calls the substraction lfun with no arguments.)
Range: `this[_.._] (Now calls `[] with two arguments, and in an expression such as x[1..] or x[..17] then the missing limits are substituted with 0 or 2147483647.)
The use of "this" and "_" was only the best we could come up with yesterday; better ideas are welcome. Anyway I think a naming scheme like the above gives more clear lfun names than the current solution with different numbers of preceding backquotes.
/ Martin Stjernholm, Roxen IS
How does that address the obscurity of lfun names like `*, ``* and ```*?
I assume
Foo lfun m_delete(int x) {} Foo _m_delete(int x) {}
would give a duplicate identifier error, at least for the lfuns on that form that already exist.
Later on this could be used to separate the namespaces of lfuns and ordinary functions, but that could otoh be accomplished with names on the form `m_delete, `indices etc. (`create instead of create. Hmm, well..)
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 18:51: Subject: Re: Pointers/lvalues
How about lfun as a prefix? Not only for the new ones, then, but as an alternative to ´ in general:
class Foo { Foo lfun +(int x) { // or lfun::+? }
Foo lfun ´+(int x) { // (``+), perhapos +', as in +-2. :-) }
Foo lfun m_delete(int x) { // as in _m_delete } }
Or perhaps 'operator', but that would be somewhat misleading in pike.
class Foo { Foo operator *(int x) { }
Foo operator m_delete(int x) { } }
/ Per Hedbor ()
It helps by getting rid of the `, which is if not hard at least harder to type than lfun, at least when you have dead-keys enabled.
Also, the current lfun naming scheme is, as you pointed out, somewhat inconsistent.
I really think that adding some kind of indication of the fact that it's a special function would be a good idea, regardless of whether or not we want a new keyword, thus allowing '_m_delete' to be called '`m_delete' if not 'lfun m_delete'.
And, yes, '_m_delete' and 'lfun m_delete' would be the same thing.
I also suggest something along the lines of
object x; x->lfun::m_delete( ... )
to access the lfuns directly.
/ Per Hedbor ()
Previous text:
2003-04-17 00:44: Subject: Re: Pointers/lvalues
How does that address the obscurity of lfun names like `*, ``* and
I assume Foo lfun m_delete(int x) {} Foo _m_delete(int x) {} would give a duplicate identifier error, at least for the lfuns on that form that already exist. Later on this could be used to separate the namespaces of lfuns and ordinary functions, but that could otoh be accomplished with names on the form `m_delete, `indices etc. (`create instead of create. Hmm, well..) / Martin Stjernholm, Roxen IS
An "lfun" keyword would be a bit more obvious prefix than `, yes.
I do however like ` better for the very personal reason that it makes those tokens easier to recognize in CC Mode. ;)
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-17 00:55: Subject: Re: Pointers/lvalues
It helps by getting rid of the `, which is if not hard at least harder to type than lfun, at least when you have dead-keys enabled.
Also, the current lfun naming scheme is, as you pointed out, somewhat inconsistent.
I really think that adding some kind of indication of the fact that it's a special function would be a good idea, regardless of whether or not we want a new keyword, thus allowing '_m_delete' to be called '`m_delete' if not 'lfun m_delete'.
And, yes, '_m_delete' and 'lfun m_delete' would be the same thing.
I also suggest something along the lines of
object x; x->lfun::m_delete( ... )
to access the lfuns directly.
/ Per Hedbor ()
I mentioned how the `&*this lfun would make it easier to implement "smart" strings, but that has nothing to do with argument passing. You must have misunderstood me.
As for examples I think I've given three already. One that shows the use of call-by-reference to modify arguments, one that shows how iterators can be used with pointer syntax (although it's of course a matter of opinion if that's an improvement, but if pointers are implemented it's a logical extension), and one that shows how pointers as return values allows them to be used in lvalue contexts.
The last part opens up interesting possibilities since iterators then can allow changes to the values they refer to. And as I've exemplified, it makes it efficient too since it allows the use of destructive `+= and similar. That's something an iterator function like set_value() can't do.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 03:07: Subject: Pointers/lvalues
Perhaps I've been a bit singleminded about "smart" strings, presented in the original text. As I understood it you would send in a reference to SmartObject->string_variable whereas I suggested to make the entire SmartObject even "smarter" and send it in instead. As for the fishyness of `= I agree that it makes things to strange to be comfortable.
The reason why I've focused on this practical problem is because it is one of the few that has been presented as a reason for introducing pointers. Obviously there has to be something good about it since most C-programmers find it a good idea, I just awaits a nice example. The most positive thing so far is that if the new lfuns are added there has to be an updated manual, because there is no way that people are going to figure out what `_&this means on their own. This paragraph sounds moodier than I intended, but you can pretend that it is more gentle and nice instead of having me rewrite it.
/ Martin Nilsson (har bott i google)
If so, then I remain unconvinced it is a good idea. E.g. "call-by-reference to modify arguments" is in itself not a goal, since it doesn't achieve anything. You can however use this feature to implement multi valued functions that addresses some issues that the current solution has. My opinion is that these "higher" problem should be addressed and solutions thought out for them. An impulse solution (that will not do) for mutli valued functions is to name the return values:
([ "file": string f, "crc32": int e ]) = do_shady_things();
This is a bad solution (unless additional magic is added) since it still makes the difference between single valued and multi valued functions to big. You would like to extend a single value function to return more values without having to change existing code.
/ Martin Nilsson (har bott i google)
Previous text:
2003-04-16 15:50: Subject: Pointers/lvalues
I mentioned how the `&*this lfun would make it easier to implement "smart" strings, but that has nothing to do with argument passing. You must have misunderstood me.
As for examples I think I've given three already. One that shows the use of call-by-reference to modify arguments, one that shows how iterators can be used with pointer syntax (although it's of course a matter of opinion if that's an improvement, but if pointers are implemented it's a logical extension), and one that shows how pointers as return values allows them to be used in lvalue contexts.
The last part opens up interesting possibilities since iterators then can allow changes to the values they refer to. And as I've exemplified, it makes it efficient too since it allows the use of destructive `+= and similar. That's something an iterator function like set_value() can't do.
/ Martin Stjernholm, Roxen IS
I note that you only dismiss one of three reasons I've given, and at least one of the other has in my opinion enough merit to warrant the feature by itself.
My opinion is that these "higher" problem should be addressed and solutions thought out for them.
I can agree with that. It doesn't solve the multi-return problem in a really satisfactory way.
There can however be other uses for passing pointers, such as functions that conditionally modify some arguments, like sscanf when the input ends short, or needs to look at their old values. In such cases a multi-return system isn't really satisfactory since the same argument would be required to be passed as an argument and used again as the target of a return value.
An impulse solution (that will not do) for mutli valued functions is to name the return values:
([ "file": string f, "crc32": int e ]) = do_shady_things();
I don't see how this would improve anything; it's just as lacking in the possibility to provide good typing, and it's even more inefficient, assuming that a mapping would be used as it looks like. Perhaps I'm missing something.
Syntactic and compatibility problems aside, I'd go with something like this:
[string, int] do_shady_things() { return ["foo", 17]; }
... [string s, int e] = do_shady_things();
On the C level, the multiple return values would simply be pushed on the stack and the functions would return the number of pushed values.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 17:18: Subject: Pointers/lvalues
If so, then I remain unconvinced it is a good idea. E.g. "call-by-reference to modify arguments" is in itself not a goal, since it doesn't achieve anything. You can however use this feature to implement multi valued functions that addresses some issues that the current solution has. My opinion is that these "higher" problem should be addressed and solutions thought out for them. An impulse solution (that will not do) for mutli valued functions is to name the return values:
([ "file": string f, "crc32": int e ]) = do_shady_things();
This is a bad solution (unless additional magic is added) since it still makes the difference between single valued and multi valued functions to big. You would like to extend a single value function to return more values without having to change existing code.
/ Martin Nilsson (har bott i google)
Syntactic and compatibility problems aside, I'd go with something like this:
[string, int] do_shady_things() { return ["foo", 17]; }
... [string s, int e] = do_shady_things();
The general idea of this is much better than "returning an array" since it clearly defines WHAT is returned and can have checking that the right stuff actually IS returned.
/ David Hedbor
Previous text:
2003-04-16 17:47: Subject: Pointers/lvalues
I note that you only dismiss one of three reasons I've given, and at least one of the other has in my opinion enough merit to warrant the feature by itself.
My opinion is that these "higher" problem should be addressed and solutions thought out for them.
I can agree with that. It doesn't solve the multi-return problem in a really satisfactory way.
There can however be other uses for passing pointers, such as functions that conditionally modify some arguments, like sscanf when the input ends short, or needs to look at their old values. In such cases a multi-return system isn't really satisfactory since the same argument would be required to be passed as an argument and used again as the target of a return value.
An impulse solution (that will not do) for mutli valued functions is to name the return values:
([ "file": string f, "crc32": int e ]) = do_shady_things();
I don't see how this would improve anything; it's just as lacking in the possibility to provide good typing, and it's even more inefficient, assuming that a mapping would be used as it looks like. Perhaps I'm missing something.
Syntactic and compatibility problems aside, I'd go with something like this:
[string, int] do_shady_things() { return ["foo", 17]; }
... [string s, int e] = do_shady_things();
On the C level, the multiple return values would simply be pushed on the stack and the functions would return the number of pushed values.
/ Martin Stjernholm, Roxen IS
Now all we need is an _destruct lfun. :-)
/ Marcus Comstedt (ACROSS) (Hail Ilpalazzo!)
Previous text:
2003-04-16 02:26: Subject: Pointers/lvalues
If I understand you correctly, your `= lfun would imply that an assignment to a variable becomes dependent on the old value in it. That's not something I'd expect of an assignment; in my view an assignment operates on a variable, not the value in it. It'd make objects too slippery for my taste:
object x = a_fishy_object; ... x = a_nice_object; if (x->is_fishy_object) { werror ("Foo?\n"); x = 0; // Begone! if (objectp (x) && x->is_fishy_object) { werror ("Arrgh!\n"); destruct (x); // Die, sucker! } }
If lfuns could be added to variables then a `= would make sense.
Anyway, how would such a thing help simulating pass-by-reference on the calling side? Where would the object instance with the `= come from?
/ Martin Stjernholm, Roxen IS
I agree this much: ||
The lack of pointers is a major disadvantage right now at times. I often use mappings or dummy objects to implement pointers now, but that has a rather large overhead.
And using reference(x) instead of &x might be good for the beginner (but I am somewhat dubious), but I don't think it makes the code more readable, really.
If nothing else, more or less all programmers know the C-pointer syntax.
void silly( int *q ) { *q = 10; }
int var; silly( &var );
void silly( pointer(int) q ) { dereference(q) = 10; }
int var; silly( reference(var) );
/ Per Hedbor ()
Previous text:
2003-04-16 01:33: Subject: Pointers/lvalues
I am this sceptical: |------------------------------|
/ Martin Nilsson (har bott i google)
int *foo <-- good if we use pointers int &foo <-- good if we use references
Mind you, if we actually implement _pointers_, sscanf should most likely change to use them as well. Overall, the pointer syntax is more complex than references BUT it is more clear in the code (i.e if you call a method that takes a pointer, you need to specifically make the variable a pointer).
I'd really rather avoid sscanf("%d, %f", &it, &ft). But alas, 'int *foo' is better than 'ref(int) foo', 'pointer(int) foo' or similar constructions. Pike IS C/C++ like, so why invent a new _syntax_ when there's already a couple that are widely used?
/ David Hedbor
Previous text:
2003-04-16 01:44: Subject: Pointers/lvalues
I agree this much: ||
The lack of pointers is a major disadvantage right now at times. I often use mappings or dummy objects to implement pointers now, but that has a rather large overhead.
And using reference(x) instead of &x might be good for the beginner (but I am somewhat dubious), but I don't think it makes the code more readable, really.
If nothing else, more or less all programmers know the C-pointer syntax.
void silly( int *q ) { *q = 10; }
int var; silly( &var );
void silly( pointer(int) q ) { dereference(q) = 10; }
int var; silly( reference(var) );
/ Per Hedbor ()
Personally I don't have any strong opinion on the syntax for pointer types; I used pointer(foo) only because it was the closest analogy to the current syntax.
Even if pointers are introduced I don't see the point in changing sscanf and reduce it to a normal function. It's already very convenient, for instance in the way one can declare variables directly in the argument list:
if (sscanf (str, "%d %d %s", int a, int b, string rest) == 3) { ... do stuff with a, b and rest ... }
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 02:50: Subject: Pointers/lvalues
int *foo <-- good if we use pointers int &foo <-- good if we use references
Mind you, if we actually implement _pointers_, sscanf should most likely change to use them as well. Overall, the pointer syntax is more complex than references BUT it is more clear in the code (i.e if you call a method that takes a pointer, you need to specifically make the variable a pointer).
I'd really rather avoid sscanf("%d, %f", &it, &ft). But alas, 'int *foo' is better than 'ref(int) foo', 'pointer(int) foo' or similar constructions. Pike IS C/C++ like, so why invent a new _syntax_ when there's already a couple that are widely used?
/ David Hedbor
Shouldn't that be possible for your reference args as well? If it's conveninent for sscanf, I don't see why it couldn't be convenient for other functions as well.
/ Marcus Comstedt (ACROSS) (Hail Ilpalazzo!)
Previous text:
2003-04-16 16:14: Subject: Pointers/lvalues
Personally I don't have any strong opinion on the syntax for pointer types; I used pointer(foo) only because it was the closest analogy to the current syntax.
Even if pointers are introduced I don't see the point in changing sscanf and reduce it to a normal function. It's already very convenient, for instance in the way one can declare variables directly in the argument list:
if (sscanf (str, "%d %d %s", int a, int b, string rest) == 3) { ... do stuff with a, b and rest ... }
/ Martin Stjernholm, Roxen IS
Yes, it'd be possible to make the compiler interpret
foo (int a);
as
int a; foo (&a);
What's harder is to simulate the way any argument to sscanf is passed by reference since that'd require strong typing for functions.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 16:19: Subject: Pointers/lvalues
Shouldn't that be possible for your reference args as well? If it's conveninent for sscanf, I don't see why it couldn't be convenient for other functions as well.
/ Marcus Comstedt (ACROSS) (Hail Ilpalazzo!)
On Wed, Apr 16, 2003 at 04:15:01PM +0200, Martin Stjernholm, Roxen IS @ Pike developers forum wrote:
I used pointer(foo) only because it was the closest analogy to the current syntax.
which is why i like it too, though i'd suggest ref(foo) to avoid the negative stigma of "pointer".
if (sscanf (str, "%d %d %s", int a, int b, string rest) == 3) {
how is this more convenient than: int a, b; string rest; if (sscanf (str, "%d %d %s", a, b, rest) ...
it doesn't save much (if any) typing. it makes the code harder to read, because it is not abvious in which scope the variables are declared.
(side note: the same is true for declarations in for and foreach, but there it would at least theoretically possible to restrict the scope of the declaration to the inside of the blocks that for and foreach execute.)
greetings, martin.
The main problem with "ref" is that it'd cause considerably bigger migration problems to reserve as a keyword than "pointer", I suspect.
how is this more convenient than: int a, b; string rest; if (sscanf (str, "%d %d %s", a, b, rest) ...
That's not the same; the identifiers only exist in the if statement just like they do in for and foreach. So the equivalent of
if (sscanf (str, "int:%d", int x) == 1) return ({INT_TYPE, x}); else if (sscanf (str, "float:%f", float x) == 1) return ({FLOAT_TYPE, x}); else if (sscanf (str, "string:%s", string x) == 1) return ({STRING_TYPE, x});
is
{ int x; if (sscanf (str, "int:%d", x) == 1) return ({INT_TYPE, x}); else { float x; if (sscanf (str, "float:%f", x) == 1) return ({FLOAT_TYPE, x}); else { string x; if (sscanf (str, "string:%s", x) == 1) return ({STRING_TYPE, x}); } } }
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 16:32: Subject: Re: Pointers/lvalues
On Wed, Apr 16, 2003 at 04:15:01PM +0200, Martin Stjernholm, Roxen IS @ Pike developers forum wrote:
I used pointer(foo) only because it was the closest analogy to the current syntax.
which is why i like it too, though i'd suggest ref(foo) to avoid the negative stigma of "pointer".
if (sscanf (str, "%d %d %s", int a, int b, string rest) == 3) {
how is this more convenient than: int a, b; string rest; if (sscanf (str, "%d %d %s", a, b, rest) ...
it doesn't save much (if any) typing. it makes the code harder to read, because it is not abvious in which scope the variables are declared.
(side note: the same is true for declarations in for and foreach, but there it would at least theoretically possible to restrict the scope of the declaration to the inside of the blocks that for and foreach execute.)
greetings, martin.
/ Brevbäraren
What would you primarily use pointers for?
Multiple return values? Call-by-reference? Iteration? Something else?
I personally don't like laying down in the path of progress, (especially if it's moving very fast) but I think pointers would be a step backwards for Pike. However, I will reserve final judgement until I figure out what it's good for. God knows I've implemented a few dubious things in Pike. :)
/ Fredrik (Naranek) Hubinette (Real Build Master)
Previous text:
2003-04-16 01:44: Subject: Pointers/lvalues
I agree this much: ||
The lack of pointers is a major disadvantage right now at times. I often use mappings or dummy objects to implement pointers now, but that has a rather large overhead.
And using reference(x) instead of &x might be good for the beginner (but I am somewhat dubious), but I don't think it makes the code more readable, really.
If nothing else, more or less all programmers know the C-pointer syntax.
void silly( int *q ) { *q = 10; }
int var; silly( &var );
void silly( pointer(int) q ) { dereference(q) = 10; }
int var; silly( reference(var) );
/ Per Hedbor ()
In my opinion call-by-reference can be very nice sometimes, mostly in cases where you need to return multiple values. I do of course know that you can return an array and all that, but it's really rather clunky, and quite fragile (return the wrong size array...).
/ David Hedbor
Previous text:
2003-04-16 03:07: Subject: Pointers/lvalues
What would you primarily use pointers for?
Multiple return values? Call-by-reference? Iteration? Something else?
I personally don't like laying down in the path of progress, (especially if it's moving very fast) but I think pointers would be a step backwards for Pike. However, I will reserve final judgement until I figure out what it's good for. God knows I've implemented a few dubious things in Pike. :)
/ Fredrik (Naranek) Hubinette (Real Build Master)
God knows I've implemented a few dubious things in Pike. :)
Examples, please ! ;-))
(no, seriously, I'm quite curious...)
/rjb
/ rjb
Previous text:
2003-04-16 03:07: Subject: Pointers/lvalues
What would you primarily use pointers for?
Multiple return values? Call-by-reference? Iteration? Something else?
I personally don't like laying down in the path of progress, (especially if it's moving very fast) but I think pointers would be a step backwards for Pike. However, I will reserve final judgement until I figure out what it's good for. God knows I've implemented a few dubious things in Pike. :)
/ Fredrik (Naranek) Hubinette (Real Build Master)
Automap is the first thing that comes to mind. Other dubious things: the dot operator -> falling back on using [] master->resolve() multisets
The list could be made much longer I'm sure... :)
/ Fredrik (Naranek) Hubinette (Real Build Master)
Previous text:
2003-04-16 14:45: Subject: Pointers/lvalues
God knows I've implemented a few dubious things in Pike. :)
Examples, please ! ;-))
(no, seriously, I'm quite curious...)
/rjb
/ rjb
I've always been slightly annoyed at all the master()->stuff. I feel the master ought to be more invisible then that.
/ Mirar
Previous text:
2003-04-16 21:07: Subject: Pointers/lvalues
Automap is the first thing that comes to mind. Other dubious things: the dot operator -> falling back on using [] master->resolve() multisets
The list could be made much longer I'm sure... :)
/ Fredrik (Naranek) Hubinette (Real Build Master)
interesting.
for automap, I guess it's little benefit for too much implementation effort (?) multisets: not bad per se, just totally superfluous. But what's wrong with the dot "operator"?
/ rjb
Previous text:
2003-04-16 21:07: Subject: Pointers/lvalues
Automap is the first thing that comes to mind. Other dubious things: the dot operator -> falling back on using [] master->resolve() multisets
The list could be made much longer I'm sure... :)
/ Fredrik (Naranek) Hubinette (Real Build Master)
multisets: not bad per se, just totally superfluous.
Multisets per se are nice. Past Pike multisets have been mostly useless because their performance sucks compared to mappings. This should be better with the new multisets though.
/ David Hedbor
Previous text:
2003-04-17 09:31: Subject: Pointers/lvalues
interesting.
for automap, I guess it's little benefit for too much implementation effort (?) multisets: not bad per se, just totally superfluous. But what's wrong with the dot "operator"?
/ rjb
All of the things I listed are design decisions which I felt were good at the time but came back and bit me later for one reason or another.
The dot operator is quirky because it uses indexing, which makes it harder to overload than it needs to be. Also, I have later realized that having a compile-time index operator is pretty much superflous as the regular index operator could do the same job and free up the dot operator for something useful. (Like adding methods to strings and arrays.)
The -> falling back on [] is really the same story. While it's terribly handy to write x->foo when you mean x["foo"], it's not very handy when you want to add methods to mappings.
As for master->resolve(), well, at the time I thought it would be cool if it was possible to write your own master object and load objects from other sources or change how symbols are resolved. However, doing so would of course change the language and it wouldn't be "Pike" anymore, would it? Also, going through the master when doing object loading has caused all sorts of problems with canonical naming, object serialization and cyclic references in compilations. Go figure...
Then there are multisets, which are really sets, which are actually pretty much arrays. For some reason I felt that it was important that the and/or/add operators had a logical consistent behaviour when the same element was present more than once in a multiset. However, I have yet to hear of an example of when this is actually useful.
Last, but not least: Automap was very hotly debated, and everybody had their own idea of how it could/ought to work, or weather it was a good idea at all. In the end, I just gave up on the discussion and implemented my idea, even though I knew it had some flaws. I kind of like how it came out, but I know some people probably don't agree.
/ Fredrik (Naranek) Hubinette (Real Build Master)
Previous text:
2003-04-17 09:31: Subject: Pointers/lvalues
interesting.
for automap, I guess it's little benefit for too much implementation effort (?) multisets: not bad per se, just totally superfluous. But what's wrong with the dot "operator"?
/ rjb
Multiple return values? Call-by-reference?
Yes, call-by-reference where modification of the arguments is wanted; pike uses call-by-reference extensively already but it doesn't allow that. Pointers as argument would typically work as a replacement for multiple return values. It offers the benefit of accurate typing compared to the current implementation of multiple return values, and it's also slightly more efficient since dynamic allocation of short lived arrays is avoided.
Iteration?
Not really; the iterators already covers that. It's mostly a matter of syntactic sugar for dereferencing them.
Something else?
Yes, the possibility to return pointers that can be used in lvalue contexts. This is very interesting for collections and iterators. I.e. `[]= can be replaced with `&[] in collections where the values are modifiable and it would work efficiently in cases like foo[17] += ({0}), foo[17] &= (<1,2,3>) etc. The alternative approach to allow optimization of such operations is to introduce a plethora of lfuns like `[]+=, `[]&=, `[]*=, etc.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 03:07: Subject: Pointers/lvalues
What would you primarily use pointers for?
Multiple return values? Call-by-reference? Iteration? Something else?
I personally don't like laying down in the path of progress, (especially if it's moving very fast) but I think pointers would be a step backwards for Pike. However, I will reserve final judgement until I figure out what it's good for. God knows I've implemented a few dubious things in Pike. :)
/ Fredrik (Naranek) Hubinette (Real Build Master)
When doing call-by-reference, you will still need to allocate an lvalue, and all references to that variables will have to do an extra indirection, so I'm not sure that it will actually be any faster.
The second argument seems like a good one though. Although, I would mostly go for the simplifications that could be made in the compiler since x+=y; could be compiled as _tmp=&x; *_tmp=*_tmp+y;
/ Fredrik (Naranek) Hubinette (Real Build Master)
Previous text:
2003-04-16 16:07: Subject: Pointers/lvalues
Multiple return values? Call-by-reference?
Yes, call-by-reference where modification of the arguments is wanted; pike uses call-by-reference extensively already but it doesn't allow that. Pointers as argument would typically work as a replacement for multiple return values. It offers the benefit of accurate typing compared to the current implementation of multiple return values, and it's also slightly more efficient since dynamic allocation of short lived arrays is avoided.
Iteration?
Not really; the iterators already covers that. It's mostly a matter of syntactic sugar for dereferencing them.
Something else?
Yes, the possibility to return pointers that can be used in lvalue contexts. This is very interesting for collections and iterators. I.e. `[]= can be replaced with `&[] in collections where the values are modifiable and it would work efficiently in cases like foo[17] += ({0}), foo[17] &= (<1,2,3>) etc. The alternative approach to allow optimization of such operations is to introduce a plethora of lfuns like `[]+=, `[]&=, `[]*=, etc.
/ Martin Stjernholm, Roxen IS
1 and 3, mainly, and for 1 mainly by using 2.
And for 3, mainly to change the value of things while you are iterating over them. That can arguably be implemented in some other way than references/pointers.
But references does have the advantage that they are fairly well known (they exists in most languages)
/ Per Hedbor ()
Previous text:
2003-04-16 03:07: Subject: Pointers/lvalues
What would you primarily use pointers for?
Multiple return values? Call-by-reference? Iteration? Something else?
I personally don't like laying down in the path of progress, (especially if it's moving very fast) but I think pointers would be a step backwards for Pike. However, I will reserve final judgement until I figure out what it's good for. God knows I've implemented a few dubious things in Pike. :)
/ Fredrik (Naranek) Hubinette (Real Build Master)
I agree this much: ||
This is starting to annoy me. If one wants to express a measurement, one ought to mention the scale. I suggest that the bars be drawn as ||---------------------| (min) |---------------------|| (max) |----||----------------| (not so positive)
Thus everyone can see exactly how positive one is on the given scale.
;)
/ Marcus Agehall (Trådlös)
Previous text:
2003-04-16 01:44: Subject: Pointers/lvalues
I agree this much: ||
The lack of pointers is a major disadvantage right now at times. I often use mappings or dummy objects to implement pointers now, but that has a rather large overhead.
And using reference(x) instead of &x might be good for the beginner (but I am somewhat dubious), but I don't think it makes the code more readable, really.
If nothing else, more or less all programmers know the C-pointer syntax.
void silly( int *q ) { *q = 10; }
int var; silly( &var );
void silly( pointer(int) q ) { dereference(q) = 10; }
int var; silly( reference(var) );
/ Per Hedbor ()
But opinion is not only one-dimensional. You must take into consideration how strong the opinion is as well.
strong opinion a ^ a) a strong negative opinion | b) doesn't care negative | positive c) doesn't matter much, but positive ------+-------> | c | |b couldn't care less
/ Mirar
Previous text:
2003-04-16 10:28: Subject: Pointers/lvalues
I agree this much: ||
This is starting to annoy me. If one wants to express a measurement, one ought to mention the scale. I suggest that the bars be drawn as ||---------------------| (min) |---------------------|| (max) |----||----------------| (not so positive)
Thus everyone can see exactly how positive one is on the given scale.
;)
/ Marcus Agehall (Trådlös)
I believe that I represented the strength of the opinion illustrated by a negative-to-positive scale. It could just as well have been Likes potatoes |---||--------------| Likes CC |------------------||
Since the x-scale you suggest is described by the textual description I don't see any purpose (other than the coolnessfactor) to have a XY-scale.
Perhaps we should have a XYZ-scale where the X-scale describes how strong the opinion is, the Y-scale what the opinion is and Z-scale describes how it has changed over time:
| /...|..... | |...|..... | /||.|...... |/.|.|..... V____|____ |
/ Marcus Agehall (Trådlös)
Previous text:
2003-04-16 11:07: Subject: Pointers/lvalues
But opinion is not only one-dimensional. You must take into consideration how strong the opinion is as well.
strong opinion a ^ a) a strong negative opinion | b) doesn't care
negative | positive c) doesn't matter much, but positive ------+-------> | c | |b couldn't care less
/ Mirar
Although that scale is a big improvement, it still lacks in some areas. Take religion for example.
If the opionee for some reason is religious (say he is a firm beliver in CC, not other softdrinks) of the topic - this must somehow be described so that the less valued (of the opionee) messurment in other messurments can be easier to compare.
| /...|..... | *...|..... | /|*.*...... |/.|.*..... V____|____ |
'*' indicating relgion factor.
/ Peter Lundqvist (disjunkt)
Previous text:
2003-04-16 11:26: Subject: Pointers/lvalues
I believe that I represented the strength of the opinion illustrated by a negative-to-positive scale. It could just as well have been Likes potatoes |---||--------------| Likes CC |------------------||
Since the x-scale you suggest is described by the textual description I don't see any purpose (other than the coolnessfactor) to have a XY-scale.
Perhaps we should have a XYZ-scale where the X-scale describes how strong the opinion is, the Y-scale what the opinion is and Z-scale describes how it has changed over time:
| /...|..... | |...|..... | /||.|...... |/.|.|..... V____|____ |
/ Marcus Agehall (Trådlös)
And then we have the dimension of degree of understanding of positive/ negative impact. :-) Lots of scales to play with. (Rapidly running off topic too.)
/ Johan Sundström (folkskådare)
Previous text:
2003-04-16 11:07: Subject: Pointers/lvalues
But opinion is not only one-dimensional. You must take into consideration how strong the opinion is as well.
strong opinion a ^ a) a strong negative opinion | b) doesn't care
negative | positive c) doesn't matter much, but positive ------+-------> | c | |b couldn't care less
/ Mirar
On the Subversion mailing list, such a two-dimensional scheme is used. To signal negative or positive opinion, you use the characters "-" and "+". Then you indicate wheterher the opinion is strong or weak with "1" or "0" respectively. So your points a and c correspond to "-1" and "+0". For the b case, I don't think they have a syntax. If you are that uninterrested, the natural thing to do is not to voice any opinion at all, and so no syntax is needed.
/ Marcus Comstedt (ACROSS) (Hail Ilpalazzo!)
Previous text:
2003-04-16 11:07: Subject: Pointers/lvalues
But opinion is not only one-dimensional. You must take into consideration how strong the opinion is as well.
strong opinion a ^ a) a strong negative opinion | b) doesn't care
negative | positive c) doesn't matter much, but positive ------+-------> | c | |b couldn't care less
/ Mirar
&foo => Get the pointer to foo; calls `& in foo.
This seems odd to me. Wouldn't & have to be magical and return a pointer to a local or global lvalue?
/ Fredrik (Naranek) Hubinette (Real Build Master)
Previous text:
2003-04-16 00:55: Subject: Pointers/lvalues
Grubba and I started talking a bit about adding lvalues as an explicit data type in Pike. I'll jot down what we arrived at for the record (well, some of it are later thoughts by me):
An lvalue is a pointer to a variable that allows the variable to be changed (as opposed to a pointer to a function). Generic variable pointers could be implemented by adding two new svalue types: Pointer to variable in object and pointer to variable in frame (if programs get "proper" static variables in some future they would get this too).
The frame case is used for pointers to local variables in functions. Refcounting in frames is already implemented for the sake of nested functions, so returning a pointer to a local variable shouldn't be a problem.
The solution with special pointer svalue types is better than the double svalue (the thing to index followed by the index) approach currently used internally for lvalues for two reasons: Firstly the index operation is performed when the pointer is created instead of when it's dereferenced, which trigs errors closer to the source. Secondly it would avoid odd effects in various functions that doesn't expect variable pointers.
Pointers to values in arrays, mappings and multisets don't need new svalue types since their iterator objects can be used then (more on that later).
Variable pointers would be used like in C. Silly example:
int append_to_buf (Stdio.File f, pointer(string) buf) { string in = f->read(); *buf += in; return sizeof (in); }
Some new operator lfuns would be needed:
&foo => Get the pointer to foo; calls `& in foo. &foo->bar => Get the pointer to foo->bar; calls `&->("bar") in foo. &foo[bar] => Ditto for []; calls `&[](bar) in foo.
`& and ``& already exist, so the first operator above would actually become ```& with the current naming system. We're however contemplating a new system with more verbose lfun names; `&this for the new operator, `this&_ for `&, and `_&this for ``&.
The similarity between pointers and iterators is obvious, so iterators should be able to act as pointers. To accomplish that an lfun corresponding to the dereferencing * operator is necessary:
*foo => Gets the value the pointer points to; calls `*this in foo.
It's also interesting to have an lfun that returns a pointer svalue so that it can be used in an lvalue context. An example:
for (pointer ptr = &my_obj[0]; *ptr; ptr++) *ptr = reverse (*ptr); ^^^^ Here `&*this would be called to produce a pointer value which is used as lvalue.
If `&*this returns an object instead of a basic pointer svalue, `&*this would presumably be tried again, which would make it possible to produce a infinite loop if `&*this should be silly enough to return the iterator itself. A simple check for that might be a good idea.
To be symmetric with `[]= and `->=, one would add a `*this=_ instead of `&*this. I do however like `&*this better since it's then possible to do e.g.
*ptr += ({17});
with an efficient `+= operation without the need for a special `*this+=_.
A `&*this lfun makes it possible to try it in any expression where no better suited lfun is available:
foo + bar => Try: foo->`+(bar) Then: bar->``+(foo) Then: *(foo->`&*this) + bar Then: foo + *(bar->`&*this)
That could be handy when implementing e.g. "smart" strings like Locale.DeferredLocale. I don't know yet if it overall is a good idea or not, though. It's clearly not a good idea in simple assignments and arbitrary function calls.
/ Martin Stjernholm, Roxen IS
Yes, you're right. That lfun shouldn't exist.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-04-16 03:10: Subject: Pointers/lvalues
&foo => Get the pointer to foo; calls `& in foo.
This seems odd to me. Wouldn't & have to be magical and return a pointer to a local or global lvalue?
/ Fredrik (Naranek) Hubinette (Real Build Master)
I think it would be prettier if the sscanf function could be implemented in Pike (implicit references) rather then to use explicit pointer operators, but pointer operators are easier to understand and makes it clearer what's going on, and are probaby way easier to implement.
I've never really felt the need for pointers or call by reference, though. I've grown used to returning multple values via an array.
/ Mirar
Previous text:
2003-04-16 00:55: Subject: Pointers/lvalues
Grubba and I started talking a bit about adding lvalues as an explicit data type in Pike. I'll jot down what we arrived at for the record (well, some of it are later thoughts by me):
An lvalue is a pointer to a variable that allows the variable to be changed (as opposed to a pointer to a function). Generic variable pointers could be implemented by adding two new svalue types: Pointer to variable in object and pointer to variable in frame (if programs get "proper" static variables in some future they would get this too).
The frame case is used for pointers to local variables in functions. Refcounting in frames is already implemented for the sake of nested functions, so returning a pointer to a local variable shouldn't be a problem.
The solution with special pointer svalue types is better than the double svalue (the thing to index followed by the index) approach currently used internally for lvalues for two reasons: Firstly the index operation is performed when the pointer is created instead of when it's dereferenced, which trigs errors closer to the source. Secondly it would avoid odd effects in various functions that doesn't expect variable pointers.
Pointers to values in arrays, mappings and multisets don't need new svalue types since their iterator objects can be used then (more on that later).
Variable pointers would be used like in C. Silly example:
int append_to_buf (Stdio.File f, pointer(string) buf) { string in = f->read(); *buf += in; return sizeof (in); }
Some new operator lfuns would be needed:
&foo => Get the pointer to foo; calls `& in foo. &foo->bar => Get the pointer to foo->bar; calls `&->("bar") in foo. &foo[bar] => Ditto for []; calls `&[](bar) in foo.
`& and ``& already exist, so the first operator above would actually become ```& with the current naming system. We're however contemplating a new system with more verbose lfun names; `&this for the new operator, `this&_ for `&, and `_&this for ``&.
The similarity between pointers and iterators is obvious, so iterators should be able to act as pointers. To accomplish that an lfun corresponding to the dereferencing * operator is necessary:
*foo => Gets the value the pointer points to; calls `*this in foo.
It's also interesting to have an lfun that returns a pointer svalue so that it can be used in an lvalue context. An example:
for (pointer ptr = &my_obj[0]; *ptr; ptr++) *ptr = reverse (*ptr); ^^^^ Here `&*this would be called to produce a pointer value which is used as lvalue.
If `&*this returns an object instead of a basic pointer svalue, `&*this would presumably be tried again, which would make it possible to produce a infinite loop if `&*this should be silly enough to return the iterator itself. A simple check for that might be a good idea.
To be symmetric with `[]= and `->=, one would add a `*this=_ instead of `&*this. I do however like `&*this better since it's then possible to do e.g.
*ptr += ({17});
with an efficient `+= operation without the need for a special `*this+=_.
A `&*this lfun makes it possible to try it in any expression where no better suited lfun is available:
foo + bar => Try: foo->`+(bar) Then: bar->``+(foo) Then: *(foo->`&*this) + bar Then: foo + *(bar->`&*this)
That could be handy when implementing e.g. "smart" strings like Locale.DeferredLocale. I don't know yet if it overall is a good idea or not, though. It's clearly not a good idea in simple assignments and arbitrary function calls.
/ Martin Stjernholm, Roxen IS
I don't like implicit references, I think it's one of the mistakes in C++. It's ok to have one or two magic functions like sscanf, but if any pike function could do the same, the code gets a lot harder to read.
An explicit operator in the function call expression is clearer.
And it's also cool to let "int a" be a valid *expression* which evaluates to a pointer/locative.
/ Niels Möller (med röd bil)
Previous text:
2003-04-16 08:28: Subject: Pointers/lvalues
I think it would be prettier if the sscanf function could be implemented in Pike (implicit references) rather then to use explicit pointer operators, but pointer operators are easier to understand and makes it clearer what's going on, and are probaby way easier to implement.
I've never really felt the need for pointers or call by reference, though. I've grown used to returning multple values via an array.
/ Mirar
On Tue, May 13, 2003 at 02:00:02PM +0200, Niels M�ller (med r�d bil) @ Pike (-) developers forum wrote:
C++. It's ok to have one or two magic functions like sscanf, but if any pike function could do the same, the code gets a lot harder to read.
It depends on the developer, not the language itself. The Roxen code is good example of code which is hard to read, despite the "good" language :)
As of me, "easy to read" means "everybody with _basic_ knowledge of programming [in general] may undestand". So... :)
Regards, /Al
When you read the C or pike code
int x = 17; foo(x); printf("%d", x);
you can tell what the third line will do (if ever executed): It will print "17". When reading the same code in a C++ program, you don't know a damn about what happens, until you have looked up the header file declaring the function foo and checked if it contains the &-character or not. This highly nonlocal dependency for a vary basic language features such as argument passing, is very bad for the readability.
For C++, you can argue that references have other nice features (compareed to pointers, which are almost the same thing, just with a more explicit syntax), such as never being NULL, and that those advantages are important enough for sacrificing readability. I'm not convinced, but anyway, in pike you probably wouldn't get those advantages anyway.
/ Niels Möller (med röd bil)
Previous text:
2003-05-13 14:41: Subject: Re: Pointers/lvalues
On Tue, May 13, 2003 at 02:00:02PM +0200, Niels Möller (med röd bil) @ Pike (-) developers forum wrote:
C++. It's ok to have one or two magic functions like sscanf, but if any pike function could do the same, the code gets a lot harder to read.
It depends on the developer, not the language itself. The Roxen code is good example of code which is hard to read, despite the "good" language :)
As of me, "easy to read" means "everybody with _basic_ knowledge of programming [in general] may undestand". So... :)
Regards, /Al
/ Brevbäraren
For good or bad, the similar situation
mapping m = ([ "x" : 17 ]); foo(x); write("%d\n", m->x);
does not give the same guarantee. This is of course not an argument to ruin the present guarantees for the basic types. There have been some more or less patological cases doing that sort of thing in Roxen, but from my recollection it was most abused before the new RXML parser was introduced. (I would not stake anything on that, though. :)
/ Johan Sundström (folkskådare)
Previous text:
2003-05-13 15:00: Subject: Re: Pointers/lvalues
When you read the C or pike code
int x = 17; foo(x); printf("%d", x);
you can tell what the third line will do (if ever executed): It will print "17". When reading the same code in a C++ program, you don't know a damn about what happens, until you have looked up the header file declaring the function foo and checked if it contains the &-character or not. This highly nonlocal dependency for a vary basic language features such as argument passing, is very bad for the readability.
For C++, you can argue that references have other nice features (compareed to pointers, which are almost the same thing, just with a more explicit syntax), such as never being NULL, and that those advantages are important enough for sacrificing readability. I'm not convinced, but anyway, in pike you probably wouldn't get those advantages anyway.
/ Niels Möller (med röd bil)
I consider that a different problem (if it is a problem).
For C and C++, I'd like to have an alternative address-of operator that takes the address of an object, and treats the result as a pointer to const.
/ Niels Möller (med röd bil)
Previous text:
2003-05-13 15:06: Subject: Re: Pointers/lvalues
For good or bad, the similar situation
mapping m = ([ "x" : 17 ]); foo(x); write("%d\n", m->x);
does not give the same guarantee. This is of course not an argument to ruin the present guarantees for the basic types. There have been some more or less patological cases doing that sort of thing in Roxen, but from my recollection it was most abused before the new RXML parser was introduced. (I would not stake anything on that, though. :)
/ Johan Sundström (folkskådare)
On Tue, May 13, 2003 at 03:05:03PM +0200, Niels M�ller (med r�d bil) @ Pike (-) developers forum wrote:
print "17". When reading the same code in a C++ program, you don't know a damn about what happens, until you have looked up the header
Well, if this is not your own code - sure, you don't know this for sure.
I personally don'z use ambiguous constructs, but _sometimes_ it is easy to use implicit references rather than explicit, though I barely remember more than two cases when I needed this :)
Pointer type in Pike would be very good idea anyway, regardless of implementation (implicit or explicit).
Another nice feature I would like to see in Pike is "goto" operator, but I guess this is only a dream :) Yes, I know that there are 1001 ways to avoid use of "goto", but again, sometimes one "goto" may save hundreths lines of code and easier to read and understand :)
Regards, /Al
On Tue, May 13, 2003 at 03:58:11PM +0200, Alexander Demenshin wrote:
Another nice feature I would like to see in Pike is "goto" operator,
that'll be the day i start looking at alternatives to pike...
can you show some real code that you think would improve through it?
greetings, martin.
State machines.
/ Johan Sundström (folkskådare)
Previous text:
2003-05-13 16:10: Subject: Re: Pointers/lvalues
On Tue, May 13, 2003 at 03:58:11PM +0200, Alexander Demenshin wrote:
Another nice feature I would like to see in Pike is "goto" operator,
that'll be the day i start looking at alternatives to pike...
can you show some real code that you think would improve through it?
greetings, martin.
/ Brevbäraren
On Tue, May 13, 2003 at 04:25:03PM +0200, Per Hedbor () @ Pike (-) developers forum wrote:
while() works just as well for that, though. Or a jump-table (function pointers in an array or mapping or similar)
In theory, it is possible to use Turing machine to do anything you want, so why do we need anything more? :)
As I said, gotos are bit more readable and obvious. For instance:
reset: [some-setup-code] while(1) { [some processing code] if (condition) goto reset; [some processing code] } [some more code]
This fragment may be rewritten not to use goto, but it is more obvious to use it, especially where "setup-code" is huge enough and may not be separated to another function.
2Martin Baer: There is nothing evil in gotos (and those who say that goto is evil should not use computers at all - machine code uses jmps etc :)
Regards, /Al
reset: while( 1 ) { ... setup code ..... while(1) { [some processing code] if( do_reset ) continue reset: [some processing code] } [some more code] }
/ Per Hedbor ()
Previous text:
2003-05-13 18:15: Subject: Re: Pointers/lvalues
On Tue, May 13, 2003 at 04:25:03PM +0200, Per Hedbor () @ Pike (-) developers forum wrote:
while() works just as well for that, though. Or a jump-table (function pointers in an array or mapping or similar)
In theory, it is possible to use Turing machine to do anything you want, so why do we need anything more? :)
As I said, gotos are bit more readable and obvious. For instance:
reset: [some-setup-code] while(1) { [some processing code] if (condition) goto reset; [some processing code] } [some more code]
This fragment may be rewritten not to use goto, but it is more obvious to use it, especially where "setup-code" is huge enough and may not be separated to another function.
2Martin Baer: There is nothing evil in gotos (and those who say that goto is evil should not use computers at all - machine code uses jmps etc :)
Regards, /Al
/ Brevbäraren
You forgot the break; before the last brace...
/ Henrik Grubbström (Lysator)
Previous text:
2003-05-13 18:16: Subject: Re: Pointers/lvalues
reset: while( 1 ) { ... setup code ..... while(1) { [some processing code] if( do_reset ) continue reset: [some processing code] } [some more code] }
/ Per Hedbor ()
On Tue, May 13, 2003 at 06:20:06PM +0200, Per Hedbor () @ Pike (-) developers forum wrote:
if( do_reset ) continue reset:
Pike 7.4.20:
hyp:~ # cat a.pike void main(int argc, array(string) argv) { int click = 0;
reset: if (click++ > 10) exit(1); while(1) { continue reset; } } hyp:~ # pike a.pike a.pike:10:No surrounding statement labeled 'reset'.
It works if while() is labelled, though. And, if there is a "continue", what is the reason not to allow goto, then? :) Regards, /Al
Yes, that doesn't work. If you do as Per suggested it might work better for you.
/ Martin Nilsson (lambda)
Previous text:
2003-05-13 18:37: Subject: Re: Pointers/lvalues
On Tue, May 13, 2003 at 06:20:06PM +0200, Per Hedbor () @ Pike (-) developers forum wrote:
if( do_reset ) continue reset:
Pike 7.4.20:
hyp:~ # cat a.pike void main(int argc, array(string) argv) { int click = 0;
reset: if (click++ > 10) exit(1); while(1) { continue reset; } } hyp:~ # pike a.pike a.pike:10:No surrounding statement labeled 'reset'.
It works if while() is labelled, though. And, if there is a "continue", what is the reason not to allow goto, then? :)
Regards, /Al
/ Brevbäraren
On Tue, May 13, 2003 at 06:45:01PM +0200, Martin Nilsson (lambda) @ Pike (-) developers forum wrote:
Yes, that doesn't work. If you do as Per suggested it might work better for you.
Well, it will solve one particular case. But I need two loops (at least), which is not improving readability...
Regards, /Al
Of course it lessens readability when you try to emulate goto in a language that has no goto concept. The "correct" solution is to design the code differently.
/ Martin Nilsson (lambda)
Previous text:
2003-05-13 19:24: Subject: Re: Pointers/lvalues
On Tue, May 13, 2003 at 06:45:01PM +0200, Martin Nilsson (lambda) @ Pike (-) developers forum wrote:
Yes, that doesn't work. If you do as Per suggested it might work better for you.
Well, it will solve one particular case. But I need two loops (at least), which is not improving readability...
Regards, /Al
/ Brevbäraren
On Tue, May 13, 2003 at 08:35:02PM +0200, Martin Nilsson (lambda) @ Pike (-) developers forum wrote:
Of course it lessens readability when you try to emulate goto in a language that has no goto concept. The "correct" solution is to design the code differently.
The "correct" code is the code that works as expected and easy to understand.
This way, if use of "goto" improves readability and undestanding - it should be done instead of designing code to avoid something. Why to create obstacles and then avoid then, instead of going directly to the goal? :)
NB: I would like to know, if possible, the reason why "goto" is not included in Pike (except well-known "the "goto" is evil" - this is not the reason, this is like "the appless are evil because I don't like them").
Regards, /Al
Of course goto isn't evil. It is however in most situations not the best solution to use goto, hence by not providing goto the overall quality of the code will be higher. I agree that Pers solution to your problem was not a good one, but you forced him to that since your problem was faulty. By using undisclosed code blocks with arbitrary constrains you can prove almost anything, since the "real solution" might be to alter these code blocks. I would have suggested
void init() { [some-setup-code] }; init(); while(1) { [some processing code] if (condition) { init(); continue; } [some processing code] } [some more code]
but it is impossible to give a good answer unless one knows what you want to achieve with the code.
/ Martin Nilsson (lambda)
Previous text:
2003-05-13 20:51: Subject: goto
On Tue, May 13, 2003 at 08:35:02PM +0200, Martin Nilsson (lambda) @ Pike (-) developers forum wrote:
Of course it lessens readability when you try to emulate goto in a language that has no goto concept. The "correct" solution is to design the code differently.
The "correct" code is the code that works as expected and easy to understand.
This way, if use of "goto" improves readability and undestanding - it should be done instead of designing code to avoid something. Why to create obstacles and then avoid then, instead of going directly to the goal? :)
NB: I would like to know, if possible, the reason why "goto" is not included in Pike (except well-known "the "goto" is evil" - this is not the reason, this is like "the appless are evil because I don't like them").
Regards, /Al
/ Brevbäraren
One reason goto isn't implement is because it's difficult: You'd have to keep track of the stack depth at both the source and the destination to do it correctly. The current byte code generator (see docode.c) is a simple recursive thing that only keeps track of that around the source, so it'd either have to be changed into some multipass thingie or use backpatching of the generated code.
Supporting jumps into special blocks like gauge and catch would also be tricky. Especially catch since you'd first have to jump to the start of it to set up the longjmp correctly, and then have a test there to jump on to the destination.
A goto would also make it necessary to introduce some arbitrary rule to handle the well known jumping-past-initializations case.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-05-13 20:51: Subject: goto
On Tue, May 13, 2003 at 08:35:02PM +0200, Martin Nilsson (lambda) @ Pike (-) developers forum wrote:
Of course it lessens readability when you try to emulate goto in a language that has no goto concept. The "correct" solution is to design the code differently.
The "correct" code is the code that works as expected and easy to understand.
This way, if use of "goto" improves readability and undestanding - it should be done instead of designing code to avoid something. Why to create obstacles and then avoid then, instead of going directly to the goal? :)
NB: I would like to know, if possible, the reason why "goto" is not included in Pike (except well-known "the "goto" is evil" - this is not the reason, this is like "the appless are evil because I don't like them").
Regards, /Al
/ Brevbäraren
You are scaring me.
/ Peter Bortas
Previous text:
2003-04-16 00:55: Subject: Pointers/lvalues
Grubba and I started talking a bit about adding lvalues as an explicit data type in Pike. I'll jot down what we arrived at for the record (well, some of it are later thoughts by me):
An lvalue is a pointer to a variable that allows the variable to be changed (as opposed to a pointer to a function). Generic variable pointers could be implemented by adding two new svalue types: Pointer to variable in object and pointer to variable in frame (if programs get "proper" static variables in some future they would get this too).
The frame case is used for pointers to local variables in functions. Refcounting in frames is already implemented for the sake of nested functions, so returning a pointer to a local variable shouldn't be a problem.
The solution with special pointer svalue types is better than the double svalue (the thing to index followed by the index) approach currently used internally for lvalues for two reasons: Firstly the index operation is performed when the pointer is created instead of when it's dereferenced, which trigs errors closer to the source. Secondly it would avoid odd effects in various functions that doesn't expect variable pointers.
Pointers to values in arrays, mappings and multisets don't need new svalue types since their iterator objects can be used then (more on that later).
Variable pointers would be used like in C. Silly example:
int append_to_buf (Stdio.File f, pointer(string) buf) { string in = f->read(); *buf += in; return sizeof (in); }
Some new operator lfuns would be needed:
&foo => Get the pointer to foo; calls `& in foo. &foo->bar => Get the pointer to foo->bar; calls `&->("bar") in foo. &foo[bar] => Ditto for []; calls `&[](bar) in foo.
`& and ``& already exist, so the first operator above would actually become ```& with the current naming system. We're however contemplating a new system with more verbose lfun names; `&this for the new operator, `this&_ for `&, and `_&this for ``&.
The similarity between pointers and iterators is obvious, so iterators should be able to act as pointers. To accomplish that an lfun corresponding to the dereferencing * operator is necessary:
*foo => Gets the value the pointer points to; calls `*this in foo.
It's also interesting to have an lfun that returns a pointer svalue so that it can be used in an lvalue context. An example:
for (pointer ptr = &my_obj[0]; *ptr; ptr++) *ptr = reverse (*ptr); ^^^^ Here `&*this would be called to produce a pointer value which is used as lvalue.
If `&*this returns an object instead of a basic pointer svalue, `&*this would presumably be tried again, which would make it possible to produce a infinite loop if `&*this should be silly enough to return the iterator itself. A simple check for that might be a good idea.
To be symmetric with `[]= and `->=, one would add a `*this=_ instead of `&*this. I do however like `&*this better since it's then possible to do e.g.
*ptr += ({17});
with an efficient `+= operation without the need for a special `*this+=_.
A `&*this lfun makes it possible to try it in any expression where no better suited lfun is available:
foo + bar => Try: foo->`+(bar) Then: bar->``+(foo) Then: *(foo->`&*this) + bar Then: foo + *(bar->`&*this)
That could be handy when implementing e.g. "smart" strings like Locale.DeferredLocale. I don't know yet if it overall is a good idea or not, though. It's clearly not a good idea in simple assignments and arbitrary function calls.
/ Martin Stjernholm, Roxen IS
I think this is called "locatives" in the lisp world. If you have them, they're what you use for locf, setf, incf, etc.
One intersting issue is gc. If you have an object where the only references to it are locatives to some of its instance variables, do you keep the object? Or should you collect the object, and just keep the isolated cells that the locatives point to?
/ Niels Möller (med röd bil)
Previous text:
2003-04-16 00:55: Subject: Pointers/lvalues
Grubba and I started talking a bit about adding lvalues as an explicit data type in Pike. I'll jot down what we arrived at for the record (well, some of it are later thoughts by me):
An lvalue is a pointer to a variable that allows the variable to be changed (as opposed to a pointer to a function). Generic variable pointers could be implemented by adding two new svalue types: Pointer to variable in object and pointer to variable in frame (if programs get "proper" static variables in some future they would get this too).
The frame case is used for pointers to local variables in functions. Refcounting in frames is already implemented for the sake of nested functions, so returning a pointer to a local variable shouldn't be a problem.
The solution with special pointer svalue types is better than the double svalue (the thing to index followed by the index) approach currently used internally for lvalues for two reasons: Firstly the index operation is performed when the pointer is created instead of when it's dereferenced, which trigs errors closer to the source. Secondly it would avoid odd effects in various functions that doesn't expect variable pointers.
Pointers to values in arrays, mappings and multisets don't need new svalue types since their iterator objects can be used then (more on that later).
Variable pointers would be used like in C. Silly example:
int append_to_buf (Stdio.File f, pointer(string) buf) { string in = f->read(); *buf += in; return sizeof (in); }
Some new operator lfuns would be needed:
&foo => Get the pointer to foo; calls `& in foo. &foo->bar => Get the pointer to foo->bar; calls `&->("bar") in foo. &foo[bar] => Ditto for []; calls `&[](bar) in foo.
`& and ``& already exist, so the first operator above would actually become ```& with the current naming system. We're however contemplating a new system with more verbose lfun names; `&this for the new operator, `this&_ for `&, and `_&this for ``&.
The similarity between pointers and iterators is obvious, so iterators should be able to act as pointers. To accomplish that an lfun corresponding to the dereferencing * operator is necessary:
*foo => Gets the value the pointer points to; calls `*this in foo.
It's also interesting to have an lfun that returns a pointer svalue so that it can be used in an lvalue context. An example:
for (pointer ptr = &my_obj[0]; *ptr; ptr++) *ptr = reverse (*ptr); ^^^^ Here `&*this would be called to produce a pointer value which is used as lvalue.
If `&*this returns an object instead of a basic pointer svalue, `&*this would presumably be tried again, which would make it possible to produce a infinite loop if `&*this should be silly enough to return the iterator itself. A simple check for that might be a good idea.
To be symmetric with `[]= and `->=, one would add a `*this=_ instead of `&*this. I do however like `&*this better since it's then possible to do e.g.
*ptr += ({17});
with an efficient `+= operation without the need for a special `*this+=_.
A `&*this lfun makes it possible to try it in any expression where no better suited lfun is available:
foo + bar => Try: foo->`+(bar) Then: bar->``+(foo) Then: *(foo->`&*this) + bar Then: foo + *(bar->`&*this)
That could be handy when implementing e.g. "smart" strings like Locale.DeferredLocale. I don't know yet if it overall is a good idea or not, though. It's clearly not a good idea in simple assignments and arbitrary function calls.
/ Martin Stjernholm, Roxen IS
The object shouldn't be freed completely in any case, unless the locative is weak. The gc would have to do more bookkeeping to perform partial freeing as you suggest, and I can't see any big advantage with it.
Well, there's one a bit similar case where it would be useful: In the current implementation of nested functions, if a function accesses one variable in a surrounding one then the complete frame of the surrounding function remains. That means that many references to temporary stuff remains after the function returns, which leads to garbage that isn't obvious. If function frames were objects then a partial-free scheme would be a solution to this. It would however have to be done directly when the function returns; doing it in the gc would still be awkward.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-05-13 13:37: Subject: Pointers/lvalues
I think this is called "locatives" in the lisp world. If you have them, they're what you use for locf, setf, incf, etc.
One intersting issue is gc. If you have an object where the only references to it are locatives to some of its instance variables, do you keep the object? Or should you collect the object, and just keep the isolated cells that the locatives point to?
/ Niels Möller (med röd bil)
The object shouldn't be freed completely in any case, unless the locative is weak. The gc would have to do more bookkeeping to perform partial freeing as you suggest, and I can't see any big advantage with it.
I've seen code to do that. For a copying gc it's straight forward, perhaps it's harder for a mark&sweep like pike's. [*]
As long as there are no destroy method on the object, freeing everything but the svalues the locatives point to seems like the right thing to do (it collects memory which will never be referenced again). If also you have a destroy method, it's harder to say what's correct.
[*] One way to do it with mark&sweep may be as follows: First make sure that when a locative is dereferenced, chains of locatives are collapsed. I.e. a locative A pointing to a locative B pointing to a svalue S should behave in the same way as a locative directly to S (for this to work, locatives need to be write-once, I guess). Make the gc collaps locative chains: When it finds A, replace it with B. There need to be some protection to keep the thing from crashing if a locative chain is circular; but circularity detection in a straight linked list is easy.
So, when the gc finds an object with locatives pointing into it, but no other references, allocate a new svalue (perhaps there should be a separate allocation area for these isolated svalues). Copy the svalue from the object to a new location, and replace the old svalue with a locative pointing to the new location. Leave the object around for now.
The next gc will collapse locative chains that pass through the old object, so that it loses all the locative references, and can be deallocated completely.
/ Niels Möller (med röd bil)
Previous text:
2003-05-14 00:29: Subject: Pointers/lvalues
The object shouldn't be freed completely in any case, unless the locative is weak. The gc would have to do more bookkeeping to perform partial freeing as you suggest, and I can't see any big advantage with it.
Well, there's one a bit similar case where it would be useful: In the current implementation of nested functions, if a function accesses one variable in a surrounding one then the complete frame of the surrounding function remains. That means that many references to temporary stuff remains after the function returns, which leads to garbage that isn't obvious. If function frames were objects then a partial-free scheme would be a solution to this. It would however have to be done directly when the function returns; doing it in the gc would still be awkward.
/ Martin Stjernholm, Roxen IS
A really accurate solution is to analyze the variable dependencies in destroy() and call it as soon as all those variables run out of references. Barring that I think it's clear that it should wait until the object is completely free of references since modifications through locatives might affect destroy(). Garbage collection semantics should always be secondary to such effects of "normal" execution.
Your idea with locative chains has the disadvantage that it complicates normal dereferencing. Overall I think it'd be simpler to contain the extra complexity in the gc. It could treat the pointed-to svalues as objects of their own and put markers on them directly.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-05-14 09:52: Subject: Pointers/lvalues
The object shouldn't be freed completely in any case, unless the locative is weak. The gc would have to do more bookkeeping to perform partial freeing as you suggest, and I can't see any big advantage with it.
I've seen code to do that. For a copying gc it's straight forward, perhaps it's harder for a mark&sweep like pike's. [*]
As long as there are no destroy method on the object, freeing everything but the svalues the locatives point to seems like the right thing to do (it collects memory which will never be referenced again). If also you have a destroy method, it's harder to say what's correct.
[*] One way to do it with mark&sweep may be as follows: First make sure that when a locative is dereferenced, chains of locatives are collapsed. I.e. a locative A pointing to a locative B pointing to a svalue S should behave in the same way as a locative directly to S (for this to work, locatives need to be write-once, I guess). Make the gc collaps locative chains: When it finds A, replace it with B. There need to be some protection to keep the thing from crashing if a locative chain is circular; but circularity detection in a straight linked list is easy.
So, when the gc finds an object with locatives pointing into it, but no other references, allocate a new svalue (perhaps there should be a separate allocation area for these isolated svalues). Copy the svalue from the object to a new location, and replace the old svalue with a locative pointing to the new location. Leave the object around for now.
The next gc will collapse locative chains that pass through the old object, so that it loses all the locative references, and can be deallocated completely.
/ Niels Möller (med röd bil)
pike-devel@lists.lysator.liu.se