www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Can we drop static struct initializers?

reply Don <nospam nospam.com> writes:
Now that we have struct literals, the old C-style struct initializers 
don't seem to be necessary.
The variations with named initializers are not really implemented -- the 
example in the spec doesn't work, and most uses of them cause compiler 
segfaults or wrong code generation. EG...

struct Move{
    int D;
}
enum Move genMove = { D:4 };
immutable Move b = genMove;

It's not difficult to fix these compiler problems, but I'm just not sure 
if it's worth implementing. Maybe they should just be dropped? (The { 
field: value } style anyway).
Nov 19 2009
next sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Don (nospam nospam.com)'s article
 Now that we have struct literals, the old C-style struct initializers
 don't seem to be necessary.
 The variations with named initializers are not really implemented -- the
 example in the spec doesn't work, and most uses of them cause compiler
 segfaults or wrong code generation. EG...
 struct Move{
     int D;
 }
 enum Move genMove = { D:4 };
 immutable Move b = genMove;
 It's not difficult to fix these compiler problems, but I'm just not sure
 if it's worth implementing. Maybe they should just be dropped? (The {
 field: value } style anyway).
I'd be inclined to say go for it, because it's yet another crufty feature inherited from C that I don't use, was barely aware even existed, and has been superseded by a better feature that's unique to D. What can be done with struct initializers that can't be done just as easily with struct literals? As far as I can tell (correct me if I'm wrong), nothing. IMHO we concern ourselves with C source compatibility and ease of porting C code too much sometimes. If people want a language that's source compatible with C, they want C++ and they know where to find it. If they want a language that sacrifices backwards compatibility to get rid of tons of cruft, then that's what D is and we shouldn't be ashamed of it. If we kinda sorta care about C source compatibility, we won't attract either audience.
Nov 19 2009
parent retard <re tard.com.invalid> writes:
Fri, 20 Nov 2009 05:59:21 +0000, dsimcha wrote:

 == Quote from Don (nospam nospam.com)'s article
 Now that we have struct literals, the old C-style struct initializers
 don't seem to be necessary.
 The variations with named initializers are not really implemented --
 the example in the spec doesn't work, and most uses of them cause
 compiler segfaults or wrong code generation. EG... struct Move{
     int D;
 }
 enum Move genMove = { D:4 };
 immutable Move b = genMove;
 It's not difficult to fix these compiler problems, but I'm just not
 sure if it's worth implementing. Maybe they should just be dropped?
 (The { field: value } style anyway).
I'd be inclined to say go for it, because it's yet another crufty feature inherited from C that I don't use, was barely aware even existed, and has been superseded by a better feature that's unique to D. What can be done with struct initializers that can't be done just as easily with struct literals? As far as I can tell (correct me if I'm wrong), nothing. IMHO we concern ourselves with C source compatibility and ease of porting C code too much sometimes. If people want a language that's source compatible with C, they want C++ and they know where to find it. If they want a language that sacrifices backwards compatibility to get rid of tons of cruft, then that's what D is and we shouldn't be ashamed of it. If we kinda sorta care about C source compatibility, we won't attract either audience.
Well said.
Nov 20 2009
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Don wrote:
 Now that we have struct literals, the old C-style struct initializers 
 don't seem to be necessary.
 The variations with named initializers are not really implemented -- the 
 example in the spec doesn't work, and most uses of them cause compiler 
 segfaults or wrong code generation. EG...
 
 struct Move{
    int D;
 }
 enum Move genMove = { D:4 };
 immutable Move b = genMove;
 
 It's not difficult to fix these compiler problems, but I'm just not sure 
 if it's worth implementing. Maybe they should just be dropped? (The { 
 field: value } style anyway).
 
 
Funny, I've been thinking the same thing. Those initializers are pretty much obsolete, the only thing left is the field name thing. To keep the field name thing with the newer struct literals would require named function parameters as well, something doable but I'm not ready to do all the work to implement that yet. Or just drop the field name thing, as you suggest.
Nov 19 2009
next sibling parent Justin Johansson <no spam.com> writes:
Walter Bright wrote:
 Don wrote:
 Now that we have struct literals, the old C-style struct initializers 
 don't seem to be necessary.
 The variations with named initializers are not really implemented -- 
 the example in the spec doesn't work, and most uses of them cause 
 compiler segfaults or wrong code generation. EG...

 struct Move{
    int D;
 }
 enum Move genMove = { D:4 };
 immutable Move b = genMove;

 It's not difficult to fix these compiler problems, but I'm just not 
 sure if it's worth implementing. Maybe they should just be dropped? 
 (The { field: value } style anyway).
Funny, I've been thinking the same thing. Those initializers are pretty much obsolete, the only thing left is the field name thing. To keep the field name thing with the newer struct literals would require named function parameters as well, something doable but I'm not ready to do all the work to implement that yet. Or just drop the field name thing, as you suggest.
Would you like another tombstone like the one I made for D. typedef?
Nov 20 2009
prev sibling next sibling parent Don <nospam nospam.com> writes:
Walter Bright wrote:
 Don wrote:
 Now that we have struct literals, the old C-style struct initializers 
 don't seem to be necessary.
 The variations with named initializers are not really implemented -- 
 the example in the spec doesn't work, and most uses of them cause 
 compiler segfaults or wrong code generation. EG...

 struct Move{
    int D;
 }
 enum Move genMove = { D:4 };
 immutable Move b = genMove;

 It's not difficult to fix these compiler problems, but I'm just not 
 sure if it's worth implementing. Maybe they should just be dropped? 
 (The { field: value } style anyway).
Funny, I've been thinking the same thing. Those initializers are pretty much obsolete, the only thing left is the field name thing. To keep the field name thing with the newer struct literals would require named function parameters as well, something doable but I'm not ready to do all the work to implement that yet. Or just drop the field name thing, as you suggest.
Dropping the whole lot would make it easier to identify delegate literals. I think the code below is quite wierd. struct A { int x; } alias void delegate () B; void bar1(A a) {} void bar2(B b) {} void main() { A a = { }; B b = { }; bar1(a); // ok bar2(b); // ok bar1( {} ); // fails. And you can't even do: bar1( cast(A){} ); bar2( {} ); // ok } I suspect that there are all kinds of odd corner cases involving these guys, which haven't been explored.
Nov 20 2009
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.con> writes:
I've used static struct initializers because somehow the dmd optimizer is able
to produce more efficient code. If you replace the C-style static struct
initializers in this program with D ones (the ones like Foo(1, 2)) the program
gets slower:
http://shootout.alioth.debian.org/debian/benchmark.php?test=nbody&lang=gdc&id=1

Maybe dmd doesn't see D-style static struct initializers as true compile-time
constants, I don't know. I think LDC doesn't share this problem/limit.

Bye,
bearophile
Nov 20 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
bearophile wrote:
 I've used static struct initializers because somehow the dmd
 optimizer is able to produce more efficient code. If you replace the
 C-style static struct initializers in this program with D ones (the
 ones like Foo(1, 2)) the program gets slower: 
 http://shootout.alioth.debian.org/debian/benchmark.php?test=nbody&lang=gdc&id=1
 
 
 Maybe dmd doesn't see D-style static struct initializers as true
 compile-time constants, I don't know. I think LDC doesn't share this
 problem/limit.
The generated code should be identical. Please file a bugzilla!
Nov 20 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Walter Bright:
 The generated code should be identical. Please file a bugzilla!
The last times I have shown a benchmark here, people have answered me that the dmd backend is primitive/old, so they have implicitly told me to not bother. So as you have noticed I have stopped showing benchmarks here for a long time, and I have focused myself in helping finding performance problems in LDC (and in LLVM itself). Recently LDC devs have slowed down their development, and they have not followed my suggestions, so I'll probably work mostly on LLVM. Bye, bearophile
Nov 20 2009
parent "Craig Black" <craigblack2 cox.net> writes:
"bearophile" <bearophileHUGS lycos.com> wrote in message 
news:he61b9$2i9n$1 digitalmars.com...
 Walter Bright:
 The generated code should be identical. Please file a bugzilla!
The last times I have shown a benchmark here, people have answered me that the dmd backend is primitive/old, so they have implicitly told me to not bother. So as you have noticed I have stopped showing benchmarks here for a long time, and I have focused myself in helping finding performance problems in LDC (and in LLVM itself). Recently LDC devs have slowed down their development, and they have not followed my suggestions, so I'll probably work mostly on LLVM. Bye, bearophile
It may be frustrating trying to get others to be as performance-minded as you are, but I for one appreciated very much any contributions that will make D faster. So thanks for trying, even if your advice is ignored. -Craig
Nov 20 2009
prev sibling next sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Walter Bright, el 19 de noviembre a las 23:53 me escribiste:
It's not difficult to fix these compiler problems, but I'm just
not sure if it's worth implementing. Maybe they should just be
dropped? (The { field: value } style anyway).
Funny, I've been thinking the same thing. Those initializers are pretty much obsolete, the only thing left is the field name thing. To keep the field name thing with the newer struct literals would require named function parameters as well, something doable but I'm not ready to do all the work to implement that yet.
Is nice to read that you like the idea of having named function parameters, even when you don't have the time or don't want to implement them :) -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- Que importante, entonces en estos días de globalización refregar nuestras almas, pasarle el lampazo a nuestros corazones para alcanzar un verdadero estado de babia peperianal. -- Peperino Pómoro
Nov 20 2009
parent reply Travis Boucher <boucher.travis gmail.com> writes:
Leandro Lucarella wrote:
 Walter Bright, el 19 de noviembre a las 23:53 me escribiste:
 It's not difficult to fix these compiler problems, but I'm just
 not sure if it's worth implementing. Maybe they should just be
 dropped? (The { field: value } style anyway).
Funny, I've been thinking the same thing. Those initializers are pretty much obsolete, the only thing left is the field name thing. To keep the field name thing with the newer struct literals would require named function parameters as well, something doable but I'm not ready to do all the work to implement that yet.
Is nice to read that you like the idea of having named function parameters, even when you don't have the time or don't want to implement them :)
Whats even nicer is that dmd front end and back end are open source allowing anyone to implement them if they really want to. Of course it will be even nicerer once the back end is at a state where it can be under a less restrictive license (single user, no redistribution? seriously?).
Nov 20 2009
parent grauzone <none example.net> writes:
Travis Boucher wrote:
 Leandro Lucarella wrote:
 Walter Bright, el 19 de noviembre a las 23:53 me escribiste:
 It's not difficult to fix these compiler problems, but I'm just
 not sure if it's worth implementing. Maybe they should just be
 dropped? (The { field: value } style anyway).
Funny, I've been thinking the same thing. Those initializers are pretty much obsolete, the only thing left is the field name thing. To keep the field name thing with the newer struct literals would require named function parameters as well, something doable but I'm not ready to do all the work to implement that yet.
Is nice to read that you like the idea of having named function parameters, even when you don't have the time or don't want to implement them :)
Whats even nicer is that dmd front end and back end are open source allowing anyone to implement them if they really want to. Of course it will be even nicerer once the back end is at a state where it can be under a less restrictive license (single user, no redistribution? seriously?).
gdc and ldc connect the dmd frontend with free backends. Of course, it gets harder to send Walter a patch in this case, and I would expect that such a patch is simply ignored.
Nov 20 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Walter Bright wrote:
 Don wrote:
 Now that we have struct literals, the old C-style struct initializers 
 don't seem to be necessary.
 The variations with named initializers are not really implemented -- 
 the example in the spec doesn't work, and most uses of them cause 
 compiler segfaults or wrong code generation. EG...

 struct Move{
    int D;
 }
 enum Move genMove = { D:4 };
 immutable Move b = genMove;

 It's not difficult to fix these compiler problems, but I'm just not 
 sure if it's worth implementing. Maybe they should just be dropped? 
 (The { field: value } style anyway).
Funny, I've been thinking the same thing. Those initializers are pretty much obsolete, the only thing left is the field name thing. To keep the field name thing with the newer struct literals would require named function parameters as well, something doable but I'm not ready to do all the work to implement that yet. Or just drop the field name thing, as you suggest.
Would love to trim the book as well. My finger is on the Del button. Just say a word. Andrei
Nov 20 2009
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Andrei Alexandrescu wrote:
 Would love to trim the book as well. My finger is on the Del button. 
 Just say a word.
Unless someone comes up with "I really need field names", dump 'em (but save a backup of your work first!).
Nov 20 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Walter Bright wrote:
 Andrei Alexandrescu wrote:
 Would love to trim the book as well. My finger is on the Del button. 
 Just say a word.
Unless someone comes up with "I really need field names", dump 'em (but save a backup of your work first!).
My RIP emails to you (as with typedef) are my backup. Don't delete them :o). So, C-style arrays are gone, C-style struct initializers are gone, typedef is gone. __traits isn't feeling too well either :o). Andrei
Nov 20 2009
next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Andrei Alexandrescu wrote:
 Walter Bright wrote:
 Andrei Alexandrescu wrote:
 Would love to trim the book as well. My finger is on the Del button. 
 Just say a word.
Unless someone comes up with "I really need field names", dump 'em (but save a backup of your work first!).
My RIP emails to you (as with typedef) are my backup. Don't delete them :o). So, C-style arrays are gone, C-style struct initializers are gone, typedef is gone. __traits isn't feeling too well either :o). Andrei
what about foreach_reverse ?
Nov 20 2009
next sibling parent dsimcha <dsimcha yahoo.com> writes:
== Quote from Yigal Chripun (yigal100 gmail.com)'s article
 Andrei Alexandrescu wrote:
 Walter Bright wrote:
 Andrei Alexandrescu wrote:
 Would love to trim the book as well. My finger is on the Del button.
 Just say a word.
Unless someone comes up with "I really need field names", dump 'em (but save a backup of your work first!).
My RIP emails to you (as with typedef) are my backup. Don't delete them :o). So, C-style arrays are gone, C-style struct initializers are gone, typedef is gone. __traits isn't feeling too well either :o). Andrei
what about foreach_reverse ?
IMHO this should be dropped as a keyword, but opApplyReverse should be kept. Calling std.range.retro() with a type where iteration works via opApply, not ranges, should cause Retro.opApply to forward to opApplyReverse. This would preserve consistency between iterating over ranges and opApply types and get rid of a really ugly keyword.
Nov 20 2009
prev sibling next sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
On Fri, Nov 20, 2009 at 11:15 AM, Yigal Chripun <yigal100 gmail.com> wrote:

 what about foreach_reverse ?
What about starting a different thread?
Nov 20 2009
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Bill Baxter wrote:
 On Fri, Nov 20, 2009 at 11:15 AM, Yigal Chripun <yigal100 gmail.com> wrote:
 
 what about foreach_reverse ?
What about starting a different thread?
Sorry. I assumed we were discussing removals from D and therefore mentioned foreach_reverse as a prime candidate. I'll start a new thread.
Nov 20 2009
parent Bill Baxter <wbaxter gmail.com> writes:
On Fri, Nov 20, 2009 at 11:55 AM, Yigal Chripun <yigal100 gmail.com> wrote:
 Bill Baxter wrote:
 On Fri, Nov 20, 2009 at 11:15 AM, Yigal Chripun <yigal100 gmail.com>
 wrote:

 what about foreach_reverse ?
What about starting a different thread?
Sorry. I assumed we were discussing removals from D and therefore mentioned foreach_reverse as a prime candidate. I'll start a new thread.
No prob. Just that people interested in the topic may not see your message if it's buried under the heading of "static struct initializers". --bb
Nov 20 2009
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Walter Bright wrote:
 Andrei Alexandrescu wrote:
 Would love to trim the book as well. My finger is on the Del button. 
 Just say a word.
Unless someone comes up with "I really need field names", dump 'em (but save a backup of your work first!).
My RIP emails to you (as with typedef) are my backup. Don't delete them :o). So, C-style arrays are gone, C-style struct initializers are gone, typedef is gone. __traits isn't feeling too well either :o). Andrei
what about foreach_reverse ?
FWIW iota is written to work better with retro on floating-point numbers than foreach_reverse. However, it does one addition and one multiplication per iteration; foreach_revers only does one addition. The multiplication, however, gives iota precise semantics. Andrei
Nov 20 2009
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Yigal Chripun wrote:
 what about foreach_reverse ?
No love for foreach_reverse? <tear>
Nov 20 2009
parent reply Max Samukha <spambox d-coding.com> writes:
On Fri, 20 Nov 2009 15:30:48 -0800, Walter Bright
<newshound1 digitalmars.com> wrote:

Yigal Chripun wrote:
 what about foreach_reverse ?
No love for foreach_reverse? <tear>
And no mercy for opApply
Nov 21 2009
parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Max Samukha (spambox d-coding.com)'s article
 On Fri, 20 Nov 2009 15:30:48 -0800, Walter Bright
 <newshound1 digitalmars.com> wrote:
Yigal Chripun wrote:
 what about foreach_reverse ?
No love for foreach_reverse? <tear>
And no mercy for opApply
opApply **must** be kept!!!! It's how my parallel foreach loop works. This would be **impossible** to implement with ranges. If opApply is removed now, I will fork the language over it.
Nov 21 2009
parent reply Max Samukha <spambox d-coding.com> writes:
On Sat, 21 Nov 2009 18:51:40 +0000 (UTC), dsimcha <dsimcha yahoo.com>
wrote:

== Quote from Max Samukha (spambox d-coding.com)'s article
 On Fri, 20 Nov 2009 15:30:48 -0800, Walter Bright
 <newshound1 digitalmars.com> wrote:
Yigal Chripun wrote:
 what about foreach_reverse ?
No love for foreach_reverse? <tear>
And no mercy for opApply
opApply **must** be kept!!!! It's how my parallel foreach loop works. This would be **impossible** to implement with ranges. If opApply is removed now, I will fork the language over it.
I guess it is possible: uint[] numbers = new uint[1_000]; pool.parallel_each!((size_t i){ numbers[i] = i; })(iota(0, numbers.length)); Though I agree it's not as cute but it is faster since the delegate is called directly. Or did I miss something?
Nov 21 2009
next sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Max Samukha (spambox d-coding.com)'s article
 On Sat, 21 Nov 2009 18:51:40 +0000 (UTC), dsimcha <dsimcha yahoo.com>
 wrote:
== Quote from Max Samukha (spambox d-coding.com)'s article
 On Fri, 20 Nov 2009 15:30:48 -0800, Walter Bright
 <newshound1 digitalmars.com> wrote:
Yigal Chripun wrote:
 what about foreach_reverse ?
No love for foreach_reverse? <tear>
And no mercy for opApply
opApply **must** be kept!!!! It's how my parallel foreach loop works. This would be **impossible** to implement with ranges. If opApply is removed now, I will fork the language over it.
I guess it is possible: uint[] numbers = new uint[1_000]; pool.parallel_each!((size_t i){ numbers[i] = i; })(iota(0, numbers.length)); Though I agree it's not as cute but it is faster since the delegate is called directly. Or did I miss something?
I'm sorry, but I put a lot of work into getting parallel foreach working, and I also have a few other pieces of code that depend on opApply and could not (easily) be rewritten in terms of ranges. I feel very strongly that opApply and ranges accomplish different enough goals that they should both be kept. opApply is good when you **just** want to define foreach syntax and nothing else, with maximum flexibility as to how the foreach syntax is implemented. Ranges are good when you want to solve a superset of this problem and define iteration over your object more generally, giving up some flexibility as to how this iteration will be implemented. Furthermore, ranges don't allow for overloading based on the iteration type. For example, you can't do this with ranges: foreach(char[] line; file) {} // Recycles buffer. foreach(string line; file) {} // Doesn't recycle buffer. They also don't allow iterating over more than one variable, like: foreach(var1, var2, var3; myObject) {} Contrary to popular belief, opApply doesn't even have to be slow. Ranges can be as slow as or slower than opApply if at least one of the three functions (front, popFront, empty) is not inlined. This actually happens in practice. For example, based on reading disassemblies and the code to inline.c, neither front() nor popFront() in std.range.Take is ever inlined. If the range functions are virtual, none of them will be inlined. Just as importantly, I've confirmed by reading the disassembly that LDC is capable of inlining the loop body of opApply at optimization levels >= O3. If D becomes mainstream, D2 will eventually also be implemented on a compiler that's smart enough to do stuff like this. To remove opApply for performance reasons would be to let the capabilities of DMD's current optimizer influence long-term design decisions. If anyone sees any harm in keeping opApply other than a slightly larger language spec, please let me know. Despite its having been superseded by ranges for a subset of use cases (and this subset, I will acknowledge, is better handled by ranges), I actually think the flexibility it gives in terms of how foreach can be implemented makes it one of D's best features.
Nov 21 2009
next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
dsimcha wrote:
 == Quote from Max Samukha (spambox d-coding.com)'s article
 On Sat, 21 Nov 2009 18:51:40 +0000 (UTC), dsimcha <dsimcha yahoo.com>
 wrote:
 == Quote from Max Samukha (spambox d-coding.com)'s article
 On Fri, 20 Nov 2009 15:30:48 -0800, Walter Bright
 <newshound1 digitalmars.com> wrote:
 Yigal Chripun wrote:
 what about foreach_reverse ?
No love for foreach_reverse? <tear>
And no mercy for opApply
opApply **must** be kept!!!! It's how my parallel foreach loop works. This would be **impossible** to implement with ranges. If opApply is removed now, I will fork the language over it.
I guess it is possible: uint[] numbers = new uint[1_000]; pool.parallel_each!((size_t i){ numbers[i] = i; })(iota(0, numbers.length)); Though I agree it's not as cute but it is faster since the delegate is called directly. Or did I miss something?
I'm sorry, but I put a lot of work into getting parallel foreach working, and I also have a few other pieces of code that depend on opApply and could not (easily) be rewritten in terms of ranges. I feel very strongly that opApply and ranges accomplish different enough goals that they should both be kept. opApply is good when you **just** want to define foreach syntax and nothing else, with maximum flexibility as to how the foreach syntax is implemented. Ranges are good when you want to solve a superset of this problem and define iteration over your object more generally, giving up some flexibility as to how this iteration will be implemented. Furthermore, ranges don't allow for overloading based on the iteration type. For example, you can't do this with ranges: foreach(char[] line; file) {} // Recycles buffer. foreach(string line; file) {} // Doesn't recycle buffer. They also don't allow iterating over more than one variable, like: foreach(var1, var2, var3; myObject) {} Contrary to popular belief, opApply doesn't even have to be slow. Ranges can be as slow as or slower than opApply if at least one of the three functions (front, popFront, empty) is not inlined. This actually happens in practice. For example, based on reading disassemblies and the code to inline.c, neither front() nor popFront() in std.range.Take is ever inlined. If the range functions are virtual, none of them will be inlined. Just as importantly, I've confirmed by reading the disassembly that LDC is capable of inlining the loop body of opApply at optimization levels >= O3. If D becomes mainstream, D2 will eventually also be implemented on a compiler that's smart enough to do stuff like this. To remove opApply for performance reasons would be to let the capabilities of DMD's current optimizer influence long-term design decisions. If anyone sees any harm in keeping opApply other than a slightly larger language spec, please let me know. Despite its having been superseded by ranges for a subset of use cases (and this subset, I will acknowledge, is better handled by ranges), I actually think the flexibility it gives in terms of how foreach can be implemented makes it one of D's best features.
There are three types of iteration: internal to the container, external by index, pointer, range, etc, and a third design with co-routines (fibers) in which the container internally iterates itself and yields a single item on each call. Ranges accomplish only the external type of iteration. opApply allows for internal iteration. All three strategies have their uses and should be allowed in D.
Nov 21 2009
parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Yigal Chripun (yigal100 gmail.com)'s article
 dsimcha wrote:
 == Quote from Max Samukha (spambox d-coding.com)'s article
 On Sat, 21 Nov 2009 18:51:40 +0000 (UTC), dsimcha <dsimcha yahoo.com>
 wrote:
 == Quote from Max Samukha (spambox d-coding.com)'s article
 On Fri, 20 Nov 2009 15:30:48 -0800, Walter Bright
 <newshound1 digitalmars.com> wrote:
 Yigal Chripun wrote:
 what about foreach_reverse ?
No love for foreach_reverse? <tear>
And no mercy for opApply
opApply **must** be kept!!!! It's how my parallel foreach loop works. This
would
 be **impossible** to implement with ranges.  If opApply is removed now, I will
 fork the language over it.
I guess it is possible: uint[] numbers = new uint[1_000]; pool.parallel_each!((size_t i){ numbers[i] = i; })(iota(0, numbers.length)); Though I agree it's not as cute but it is faster since the delegate is called directly. Or did I miss something?
I'm sorry, but I put a lot of work into getting parallel foreach working, and I also have a few other pieces of code that depend on opApply and could not (easily) be rewritten in terms of ranges. I feel very strongly that opApply and ranges accomplish different enough goals that they should both be kept. opApply is good when you **just** want to define foreach syntax and nothing else, with maximum flexibility as to how the foreach syntax is implemented. Ranges are good when you want to solve a superset of this problem and define iteration over your object more generally, giving up some flexibility as to how this iteration will be implemented. Furthermore, ranges don't allow for overloading based on the iteration type. For example, you can't do this with ranges: foreach(char[] line; file) {} // Recycles buffer. foreach(string line; file) {} // Doesn't recycle buffer. They also don't allow iterating over more than one variable, like: foreach(var1, var2, var3; myObject) {} Contrary to popular belief, opApply doesn't even have to be slow. Ranges can be as slow as or slower than opApply if at least one of the three functions (front, popFront, empty) is not inlined. This actually happens in practice. For example, based on reading disassemblies and the code to inline.c, neither front() nor popFront() in std.range.Take is ever inlined. If the range functions are virtual, none of them will be inlined. Just as importantly, I've confirmed by reading the disassembly that LDC is capable of inlining the loop body of opApply at optimization levels >= O3. If D becomes mainstream, D2 will eventually also be implemented on a compiler that's smart enough to do stuff like this. To remove opApply for performance reasons would be to let the capabilities of DMD's current optimizer influence long-term design decisions. If anyone sees any harm in keeping opApply other than a slightly larger language spec, please let me know. Despite its having been superseded by ranges for a subset of use cases (and this subset, I will acknowledge, is better handled by ranges), I actually think the flexibility it gives in terms of how foreach can be implemented makes it one of D's best features.
There are three types of iteration: internal to the container, external by index, pointer, range, etc, and a third design with co-routines (fibers) in which the container internally iterates itself and yields a single item on each call. Ranges accomplish only the external type of iteration. opApply allows for internal iteration. All three strategies have their uses and should be allowed in D.
Exactly. I've said this before, but I think you said it much better. Now that Walter has agreed to keep opApply, this should really be explained somewhere in TDPL and in the online docs to clarify to newcomers why someone would choose opApply over ranges or vice-versa. External iteration is more flexible for the user of the object, internal iteration is more flexible for the designer of the object.
Nov 22 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
dsimcha wrote:
 == Quote from Yigal Chripun (yigal100 gmail.com)'s article
 dsimcha wrote:
 == Quote from Max Samukha (spambox d-coding.com)'s article
 On Sat, 21 Nov 2009 18:51:40 +0000 (UTC), dsimcha <dsimcha yahoo.com>
 wrote:
 == Quote from Max Samukha (spambox d-coding.com)'s article
 On Fri, 20 Nov 2009 15:30:48 -0800, Walter Bright
 <newshound1 digitalmars.com> wrote:
 Yigal Chripun wrote:
 what about foreach_reverse ?
No love for foreach_reverse? <tear>
And no mercy for opApply
opApply **must** be kept!!!! It's how my parallel foreach loop works. This
would
 be **impossible** to implement with ranges.  If opApply is removed now, I will
 fork the language over it.
I guess it is possible: uint[] numbers = new uint[1_000]; pool.parallel_each!((size_t i){ numbers[i] = i; })(iota(0, numbers.length)); Though I agree it's not as cute but it is faster since the delegate is called directly. Or did I miss something?
I'm sorry, but I put a lot of work into getting parallel foreach working, and I also have a few other pieces of code that depend on opApply and could not (easily) be rewritten in terms of ranges. I feel very strongly that opApply and ranges accomplish different enough goals that they should both be kept. opApply is good when you **just** want to define foreach syntax and nothing else, with maximum flexibility as to how the foreach syntax is implemented. Ranges are good when you want to solve a superset of this problem and define iteration over your object more generally, giving up some flexibility as to how this iteration will be implemented. Furthermore, ranges don't allow for overloading based on the iteration type. For example, you can't do this with ranges: foreach(char[] line; file) {} // Recycles buffer. foreach(string line; file) {} // Doesn't recycle buffer. They also don't allow iterating over more than one variable, like: foreach(var1, var2, var3; myObject) {} Contrary to popular belief, opApply doesn't even have to be slow. Ranges can be as slow as or slower than opApply if at least one of the three functions (front, popFront, empty) is not inlined. This actually happens in practice. For example, based on reading disassemblies and the code to inline.c, neither front() nor popFront() in std.range.Take is ever inlined. If the range functions are virtual, none of them will be inlined. Just as importantly, I've confirmed by reading the disassembly that LDC is capable of inlining the loop body of opApply at optimization levels >= O3. If D becomes mainstream, D2 will eventually also be implemented on a compiler that's smart enough to do stuff like this. To remove opApply for performance reasons would be to let the capabilities of DMD's current optimizer influence long-term design decisions. If anyone sees any harm in keeping opApply other than a slightly larger language spec, please let me know. Despite its having been superseded by ranges for a subset of use cases (and this subset, I will acknowledge, is better handled by ranges), I actually think the flexibility it gives in terms of how foreach can be implemented makes it one of D's best features.
There are three types of iteration: internal to the container, external by index, pointer, range, etc, and a third design with co-routines (fibers) in which the container internally iterates itself and yields a single item on each call. Ranges accomplish only the external type of iteration. opApply allows for internal iteration. All three strategies have their uses and should be allowed in D.
Exactly. I've said this before, but I think you said it much better. Now that Walter has agreed to keep opApply, this should really be explained somewhere in TDPL and in the online docs to clarify to newcomers why someone would choose opApply over ranges or vice-versa. External iteration is more flexible for the user of the object, internal iteration is more flexible for the designer of the object.
Copied this message to my todo list, thanks. Andrei
Nov 22 2009
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
dsimcha wrote:
 If anyone sees any harm in keeping opApply other than a slightly larger
language
 spec, please let me know.  Despite its having been superseded by ranges for a
 subset of use cases (and this subset, I will acknowledge, is better handled by
 ranges), I actually think the flexibility it gives in terms of how foreach can
be
 implemented makes it one of D's best features.
Thanks for writing this. It's nice to have a compelling argument, and you gave one. Looks like opApply is staying.
Nov 21 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Walter Bright wrote:
 dsimcha wrote:
 If anyone sees any harm in keeping opApply other than a slightly 
 larger language
 spec, please let me know.  Despite its having been superseded by 
 ranges for a
 subset of use cases (and this subset, I will acknowledge, is better 
 handled by
 ranges), I actually think the flexibility it gives in terms of how 
 foreach can be
 implemented makes it one of D's best features.
Thanks for writing this. It's nice to have a compelling argument, and you gave one. Looks like opApply is staying.
I think the same. Thanks, David. Andrei
Nov 22 2009
prev sibling parent Max Samukha <spambox d-coding.com> writes:
On Sat, 21 Nov 2009 20:21:54 +0000 (UTC), dsimcha <dsimcha yahoo.com>
wrote:

 I guess it is possible:
 uint[] numbers = new uint[1_000];
 pool.parallel_each!((size_t i){
         numbers[i] = i;
     })(iota(0, numbers.length));
 Though I agree it's not as cute but it is faster since the delegate is
 called directly. Or did I miss something?
I'm sorry, but I put a lot of work into getting parallel foreach working, and I also have a few other pieces of code that depend on opApply and could not (easily) be rewritten in terms of ranges. I feel very strongly that opApply and ranges accomplish different enough goals that they should both be kept.
Nothing to be sorry about. I was replying to your statement that parallel foreach cannot be implemented in terms of ranges.
opApply is good when you **just** want to define foreach syntax and nothing
else,
with maximum flexibility as to how the foreach syntax is implemented.  Ranges
are
good when you want to solve a superset of this problem and define iteration over
your object more generally, giving up some flexibility as to how this iteration
will be implemented.

Furthermore, ranges don't allow for overloading based on the iteration type. 
For
example, you can't do this with ranges:

foreach(char[] line; file) {}  // Recycles buffer.
foreach(string line; file) {}  // Doesn't recycle buffer.
Ok
They also don't allow iterating over more than one variable, like:
foreach(var1, var2, var3; myObject) {}
I guess it is a relatively rare case. And you can always provide a range returning a tuple. foreach(r; myObject.range) { writeln(r.front.var1, r.front.var2, r.front.var3); } The common case of iterating over index and value of a random access range should be supported by foreach.
Contrary to popular belief, opApply doesn't even have to be slow.  Ranges can be
as slow as or slower than opApply if at least one of the three functions (front,
popFront, empty) is not inlined.   This actually happens in practice.  For
example, based on reading disassemblies and the code to inline.c, neither
front()
nor popFront() in std.range.Take is ever inlined.
I think that is a problem with the compiler, not ranges.
 If the range functions are
virtual, none of them will be inlined.
Ok
Just as importantly, I've confirmed by reading the disassembly that LDC is
capable
of inlining the loop body of opApply at optimization levels >= O3.  If D becomes
mainstream, D2 will eventually also be implemented on a compiler that's smart
enough to do stuff like this.  To remove opApply for performance reasons would
be
to let the capabilities of DMD's current optimizer influence long-term design
decisions.
Convinced. Let's keep opApply.
Nov 23 2009
prev sibling parent reply retard <re tard.com.invalid> writes:
Sat, 21 Nov 2009 21:49:21 +0200, Max Samukha wrote:

 On Sat, 21 Nov 2009 18:51:40 +0000 (UTC), dsimcha <dsimcha yahoo.com>
 wrote:
 
== Quote from Max Samukha (spambox d-coding.com)'s article
 On Fri, 20 Nov 2009 15:30:48 -0800, Walter Bright
 <newshound1 digitalmars.com> wrote:
Yigal Chripun wrote:
 what about foreach_reverse ?
No love for foreach_reverse? <tear>
And no mercy for opApply
opApply **must** be kept!!!! It's how my parallel foreach loop works. This would be **impossible** to implement with ranges. If opApply is removed now, I will fork the language over it.
I guess it is possible: uint[] numbers = new uint[1_000]; pool.parallel_each!((size_t i){ numbers[i] = i; })(iota(0, numbers.length)); Though I agree it's not as cute but it is faster since the delegate is called directly. Or did I miss something?
Sorry for asking this stupid question but why was 'iota' chosen for that purpose? I was kind of expecting that D2 would have these extension methods which would allow writing '0.to(numbers.length)' by defining 'auto to(int begin, int end) { ... }'. I looked at wikipedia (http:// en.wikipedia.org/wiki/Iota) and they seem to give credit for the Go programming language which seems rather weird since they probably existed in D long before Go was released. I've never heard of this APL being used anywhere.
Nov 21 2009
next sibling parent dsimcha <dsimcha yahoo.com> writes:
== Quote from retard (re tard.com.invalid)'s article
 Sat, 21 Nov 2009 21:49:21 +0200, Max Samukha wrote:
 On Sat, 21 Nov 2009 18:51:40 +0000 (UTC), dsimcha <dsimcha yahoo.com>
 wrote:

== Quote from Max Samukha (spambox d-coding.com)'s article
 On Fri, 20 Nov 2009 15:30:48 -0800, Walter Bright
 <newshound1 digitalmars.com> wrote:
Yigal Chripun wrote:
 what about foreach_reverse ?
No love for foreach_reverse? <tear>
And no mercy for opApply
opApply **must** be kept!!!! It's how my parallel foreach loop works. This would be **impossible** to implement with ranges. If opApply is removed now, I will fork the language over it.
I guess it is possible: uint[] numbers = new uint[1_000]; pool.parallel_each!((size_t i){ numbers[i] = i; })(iota(0, numbers.length)); Though I agree it's not as cute but it is faster since the delegate is called directly. Or did I miss something?
Sorry for asking this stupid question but why was 'iota' chosen for that purpose? I was kind of expecting that D2 would have these extension methods which would allow writing '0.to(numbers.length)' by defining 'auto to(int begin, int end) { ... }'. I looked at wikipedia (http:// en.wikipedia.org/wiki/Iota) and they seem to give credit for the Go programming language which seems rather weird since they probably existed in D long before Go was released. I've never heard of this APL being used anywhere.
Because parallel foreach works over arbitrary ranges (though somewhat inefficiently over non-random access ranges) and iota is what's implemented now, so that's what I used in my examples. If we get extension methos and 0.to(numbers.length) returns a random access range of integers [0, numbers.length) , then this will work, too.
Nov 21 2009
prev sibling parent KennyTM~ <kennytm gmail.com> writes:
On Nov 22, 09 04:32, retard wrote:
 Sat, 21 Nov 2009 21:49:21 +0200, Max Samukha wrote:

 On Sat, 21 Nov 2009 18:51:40 +0000 (UTC), dsimcha<dsimcha yahoo.com>
 wrote:

 == Quote from Max Samukha (spambox d-coding.com)'s article
 On Fri, 20 Nov 2009 15:30:48 -0800, Walter Bright
 <newshound1 digitalmars.com>  wrote:
 Yigal Chripun wrote:
 what about foreach_reverse ?
No love for foreach_reverse?<tear>
And no mercy for opApply
opApply **must** be kept!!!! It's how my parallel foreach loop works. This would be **impossible** to implement with ranges. If opApply is removed now, I will fork the language over it.
I guess it is possible: uint[] numbers = new uint[1_000]; pool.parallel_each!((size_t i){ numbers[i] = i; })(iota(0, numbers.length)); Though I agree it's not as cute but it is faster since the delegate is called directly. Or did I miss something?
Sorry for asking this stupid question but why was 'iota' chosen for that purpose? I was kind of expecting that D2 would have these extension methods which would allow writing '0.to(numbers.length)' by defining 'auto to(int begin, int end) { ... }'. I looked at wikipedia (http:// en.wikipedia.org/wiki/Iota) and they seem to give credit for the Go programming language which seems rather weird since they probably existed in D long before Go was released. I've never heard of this APL being used anywhere.
Because C++ used iota. Although "range" is easier to understand, and I would say an C++ programmer has higher chance to know what is range(1,10) than iota(1,10). Also, "to" has been used in conversion (e.g. to!(string)(345))
Nov 21 2009
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Andrei Alexandrescu, el 20 de noviembre a las 10:42 me escribiste:
 Walter Bright wrote:
Andrei Alexandrescu wrote:
Would love to trim the book as well. My finger is on the Del
button. Just say a word.
Unless someone comes up with "I really need field names", dump 'em (but save a backup of your work first!).
My RIP emails to you (as with typedef) are my backup. Don't delete them :o). So, C-style arrays are gone, C-style struct initializers are gone,
C-style comma operator is gone, ... no, wait. I'm dreaming =P -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- No existiría el sonido del mar si faltara en la vida oreja y caracol. -- Ricardo Vaporeso. Cosquín, 1908.
Nov 20 2009
parent Bill Baxter <wbaxter gmail.com> writes:
On Sat, Nov 21, 2009 at 3:07 AM, retard <re tard.com.invalid> wrote:
 Fri, 20 Nov 2009 17:06:44 -0300, Leandro Lucarella wrote:

 So, C-style arrays are gone, C-style struct initializers are gone,
C-style comma operator is gone, ... no, wait. I'm dreaming =P
It will never die. It's a perfect construct to be used in code generation.
Yes, but as has been point out many times, it doesn't need to occupy such a plumb piece of syntactical real estate in order to be a useful code gen construct. --bb
Nov 21 2009
prev sibling parent KennyTM~ <kennytm gmail.com> writes:
On Nov 21, 09 00:15, Andrei Alexandrescu wrote:
 Walter Bright wrote:
 Don wrote:
 Now that we have struct literals, the old C-style struct initializers
 don't seem to be necessary.
 The variations with named initializers are not really implemented --
 the example in the spec doesn't work, and most uses of them cause
 compiler segfaults or wrong code generation. EG...

 struct Move{
 int D;
 }
 enum Move genMove = { D:4 };
 immutable Move b = genMove;

 It's not difficult to fix these compiler problems, but I'm just not
 sure if it's worth implementing. Maybe they should just be dropped?
 (The { field: value } style anyway).
Funny, I've been thinking the same thing. Those initializers are pretty much obsolete, the only thing left is the field name thing. To keep the field name thing with the newer struct literals would require named function parameters as well, something doable but I'm not ready to do all the work to implement that yet. Or just drop the field name thing, as you suggest.
Would love to trim the book as well. My finger is on the Del button. Just say a word. Andrei
Maybe C-style function pointer types (i.e. void(*f)(void*))? ;)
Nov 20 2009
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Don wrote:
 Now that we have struct literals, the old C-style struct initializers 
 don't seem to be necessary.
 The variations with named initializers are not really implemented -- the 
 example in the spec doesn't work, and most uses of them cause compiler 
 segfaults or wrong code generation. EG...
 
 struct Move{
    int D;
 }
 enum Move genMove = { D:4 };
 immutable Move b = genMove;
 
 It's not difficult to fix these compiler problems, but I'm just not sure 
 if it's worth implementing. Maybe they should just be dropped? (The { 
 field: value } style anyway).
Would love to. At least we can deprecate them and not mention them in TDPL. Andrei
Nov 20 2009
prev sibling next sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
On Thu, Nov 19, 2009 at 9:48 PM, Don <nospam nospam.com> wrote:
 Now that we have struct literals, the old C-style struct initializers don=
't
 seem to be necessary.
 The variations with named initializers are not really implemented -- the
 example in the spec doesn't work, and most uses of them cause compiler
 segfaults or wrong code generation. EG...

 struct Move{
 =A0 int D;
 }
 enum Move genMove =3D { D:4 };
 immutable Move b =3D genMove;

 It's not difficult to fix these compiler problems, but I'm just not sure =
if
 it's worth implementing. Maybe they should just be dropped? (The { field:
 value } style anyway).
I agree there are too many ways to make structs, but... 1) Struct literals don't work if you have an opCall for your struct. (Maybe that's not such a big deal now that structs have constructors? I haven't had a chance to look into struct constructors yet...) 2) The field:value style struct initializer is probably the closest D will ever get to named arguments. I think perhaps it should require the struct name, and be treated as a struct literal rather than static initializer: auto anS =3D S{D:4}; <=3D> auto anS =3D S(4) --bb
Nov 20 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 1) Struct literals don't work if you have an opCall for your struct.
     (Maybe that's not such a big deal now that structs have
 constructors?  I haven't had a chance to look into struct constructors
 yet...)
Worst case, you can still construct them dynamically.
 2) The field:value style struct initializer is probably the closest D
 will ever get to named arguments.  I think perhaps it should require
 the struct name, and be treated as a struct literal rather than static
 initializer:
 
       auto anS = S{D:4};   <=>   auto anS = S(4)
I think we'd need a compelling use case for why this is needed.
Nov 20 2009
next sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Fri, Nov 20, 2009 at 10:29 AM, Walter Bright
<newshound1 digitalmars.com> wrote:
 Bill Baxter wrote:
 1) Struct literals don't work if you have an opCall for your struct.
 =A0 =A0(Maybe that's not such a big deal now that structs have
 constructors? =A0I haven't had a chance to look into struct constructors
 yet...)
Worst case, you can still construct them dynamically.
 2) The field:value style struct initializer is probably the closest D
 will ever get to named arguments. =A0I think perhaps it should require
 the struct name, and be treated as a struct literal rather than static
 initializer:

 =A0 =A0 =A0auto anS =3D S{D:4}; =A0 <=3D> =A0 auto anS =3D S(4)
I think we'd need a compelling use case for why this is needed.
This is the main use case I have in mind: void runAlgo(Options opt); struct Options { bool useFrobbing =3D false; int numIters =3D 200; float tolerance =3D 1e-4; int verbosity =3D 0; // ... } runAlgo( Options{verbosity:100} ); instead of Options opt; opt.verbosity =3D 100; runAlgo(opt); --bb
Nov 20 2009
prev sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Fri, Nov 20, 2009 at 10:58 AM, Bill Baxter <wbaxter gmail.com> wrote:
 On Fri, Nov 20, 2009 at 10:29 AM, Walter Bright
 <newshound1 digitalmars.com> wrote:
 Bill Baxter wrote:
 1) Struct literals don't work if you have an opCall for your struct.
 =A0 =A0(Maybe that's not such a big deal now that structs have
 constructors? =A0I haven't had a chance to look into struct constructor=
s
 yet...)
Worst case, you can still construct them dynamically.
 2) The field:value style struct initializer is probably the closest D
 will ever get to named arguments. =A0I think perhaps it should require
 the struct name, and be treated as a struct literal rather than static
 initializer:

 =A0 =A0 =A0auto anS =3D S{D:4}; =A0 <=3D> =A0 auto anS =3D S(4)
I think we'd need a compelling use case for why this is needed.
This is the main use case I have in mind: void runAlgo(Options opt); struct Options { =A0 =A0bool useFrobbing =3D false; =A0 =A0int numIters =3D 200; =A0 =A0float tolerance =3D 1e-4; =A0 =A0int verbosity =3D 0; =A0 =A0// ... =A0} runAlgo( Options{verbosity:100} ); instead of Options opt; opt.verbosity =3D 100; runAlgo(opt);
I should say that I won't really shed a tear over loss of the current static-only {a:b} syntax. But I would like to see something similar resurrected as a fully dynamic construct for the use case of large numbers of options with good defaults for most. I see no reason the compiler shouldn't be able to turn runAlgo( Options{verbosity:100} ); into Options _tmp; _tmp.verbosity =3D 100; runAlgo( _tmp ); --bb
Nov 20 2009
prev sibling next sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
On Thu, Nov 19, 2009 at 9:48 PM, Don <nospam nospam.com> wrote:
 Now that we have struct literals, the old C-style struct initializers don=
't
 seem to be necessary.
 The variations with named initializers are not really implemented -- the
 example in the spec doesn't work, and most uses of them cause compiler
 segfaults or wrong code generation. EG...

 struct Move{
 =A0 int D;
 }
 enum Move genMove =3D { D:4 };
 immutable Move b =3D genMove;

 It's not difficult to fix these compiler problems, but I'm just not sure =
if
 it's worth implementing. Maybe they should just be dropped? (The { field:
 value } style anyway).
Here's one thing I just found: struct constructors don't work at compile-time: struct Struct { this(int _n, float _x) { n =3D _n; x =3D _x; } int n; float x; } enum A =3D Struct(1,2); // Error: cannot evaluate ((Struct __ctmp1; // ) , __ctmp1).this(1,2F) at compile time The C-style initializer works. static opCall works too. But if that bug is fixed, then I can't think of a reason to have the classic C-style no-colons syntax. --bb
Nov 20 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 Here's one thing I just found:
 struct constructors don't work at compile-time:
 
 struct Struct
 {
     this(int _n, float _x) {
         n = _n; x = _x;
     }
     int n;
     float x;
 }
 
 enum A = Struct(1,2);
 
 // Error: cannot evaluate ((Struct __ctmp1;
 // ) , __ctmp1).this(1,2F) at compile time
 
 The C-style initializer works.
 static opCall works too.
 
 But if that bug is fixed, then I can't think of a reason to have the
 classic C-style no-colons syntax.
It isn't a bug. You simply don't need constructors that progressively assign parameters to fields. Struct(1,2); works just fine without that constructor being defined.
Nov 20 2009
parent reply Bill Baxter <wbaxter gmail.com> writes:
On Fri, Nov 20, 2009 at 3:34 PM, Walter Bright
<newshound1 digitalmars.com> wrote:
 Bill Baxter wrote:
 Here's one thing I just found:
 struct constructors don't work at compile-time:

 struct Struct
 {
 =A0 =A0this(int _n, float _x) {
 =A0 =A0 =A0 =A0n =3D _n; x =3D _x;
 =A0 =A0}
 =A0 =A0int n;
 =A0 =A0float x;
 }

 enum A =3D Struct(1,2);

 // Error: cannot evaluate ((Struct __ctmp1;
 // ) , __ctmp1).this(1,2F) at compile time

 The C-style initializer works.
 static opCall works too.

 But if that bug is fixed, then I can't think of a reason to have the
 classic C-style no-colons syntax.
It isn't a bug. You simply don't need constructors that progressively ass=
ign
 parameters to fields.

 =A0 Struct(1,2);

 works just fine without that constructor being defined.
Right, but if you do define it (in order to do something extra upon initialization -- validate inputs or what have you) then it no longer works at compile time. --bb
Nov 20 2009
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 Right, but if you do define it (in order to do something extra upon
 initialization -- validate inputs or what have you) then it no longer
 works at compile time.
Right, but the static initialization then shouldn't work, either.
Nov 20 2009
parent reply Bill Baxter <wbaxter gmail.com> writes:
On Fri, Nov 20, 2009 at 4:05 PM, Walter Bright
<newshound1 digitalmars.com> wrote:
 Bill Baxter wrote:
 Right, but if you do define it (in order to do something extra upon
 initialization -- validate inputs or what have you) then it no longer
 works at compile time.
Right, but the static initialization then shouldn't work, either.
Why not? It works if you use static opCall(int,int) instead of this(int,int). --bb
Nov 20 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 On Fri, Nov 20, 2009 at 4:05 PM, Walter Bright
 <newshound1 digitalmars.com> wrote:
 Bill Baxter wrote:
 Right, but if you do define it (in order to do something extra upon
 initialization -- validate inputs or what have you) then it no longer
 works at compile time.
Right, but the static initialization then shouldn't work, either.
Why not? It works if you use static opCall(int,int) instead of this(int,int).
Because if you need runtime execution to initialize, a back door to statically initialize it looks like a bug.
Nov 20 2009
next sibling parent Don <nospam nospam.com> writes:
Walter Bright wrote:
 Bill Baxter wrote:
 On Fri, Nov 20, 2009 at 4:05 PM, Walter Bright
 <newshound1 digitalmars.com> wrote:
 Bill Baxter wrote:
 Right, but if you do define it (in order to do something extra upon
 initialization -- validate inputs or what have you) then it no longer
 works at compile time.
Right, but the static initialization then shouldn't work, either.
Why not? It works if you use static opCall(int,int) instead of this(int,int).
Because if you need runtime execution to initialize, a back door to statically initialize it looks like a bug.
No, it's a known bug. The problem is that a constructor call A a(b); gets changed into (A __a;, __a.opThis()) The scope of __a is whereever it's called from. In the case of const int X = foo(A a); __ is in global scope, so it's a static variable, and therefore can't be used in CTFE! But of course it's not a genuine static, it's just an artifact of the transformation. Exactly the same problem happens with variadic arguments. And for pretty much the same reason, you get an ICE if you declare a struct with a constructor as a default function parameter. I'm not sure, but it could be that int parameters also become shared variables? One way to fix it might be to introduce a flag such that compiler-generated statics are marked so that they can distinguished from real statics.
Nov 20 2009
prev sibling parent reply BCS <none anon.com> writes:
Hello Walter,

 Bill Baxter wrote:
 
 On Fri, Nov 20, 2009 at 4:05 PM, Walter Bright
 <newshound1 digitalmars.com> wrote:
 Bill Baxter wrote:
 
 Right, but if you do define it (in order to do something extra upon
 initialization -- validate inputs or what have you) then it no
 longer works at compile time.
 
Right, but the static initialization then shouldn't work, either.
Why not? It works if you use static opCall(int,int) instead of this(int,int).
Because if you need runtime execution to initialize, a back door to statically initialize it looks like a bug.
who said anything about needing runtime? If the constructor can be evaluated as CTFE with it's only result being the struct getting set up, why not let it. I can think of serval useful thing to do with that (validation, denormalization, etc.)
Nov 23 2009
parent Don <nospam nospam.com> writes:
BCS wrote:
 Hello Walter,
 
 Bill Baxter wrote:

 On Fri, Nov 20, 2009 at 4:05 PM, Walter Bright
 <newshound1 digitalmars.com> wrote:
 Bill Baxter wrote:

 Right, but if you do define it (in order to do something extra upon
 initialization -- validate inputs or what have you) then it no
 longer works at compile time.
Right, but the static initialization then shouldn't work, either.
Why not? It works if you use static opCall(int,int) instead of this(int,int).
Because if you need runtime execution to initialize, a back door to statically initialize it looks like a bug.
who said anything about needing runtime? If the constructor can be evaluated as CTFE with it's only result being the struct getting set up, why not let it. I can think of serval useful thing to do with that (validation, denormalization, etc.)
Read my post. It's just a compiler bug.
Nov 23 2009
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Fri, Nov 20, 2009 at 3:34 PM, Walter Bright
 <newshound1 digitalmars.com> wrote:
 Bill Baxter wrote:
 Here's one thing I just found:
 struct constructors don't work at compile-time:

 struct Struct
 {
    this(int _n, float _x) {
        n = _n; x = _x;
    }
    int n;
    float x;
 }

 enum A = Struct(1,2);

 // Error: cannot evaluate ((Struct __ctmp1;
 // ) , __ctmp1).this(1,2F) at compile time

 The C-style initializer works.
 static opCall works too.

 But if that bug is fixed, then I can't think of a reason to have the
 classic C-style no-colons syntax.
It isn't a bug. You simply don't need constructors that progressively assign parameters to fields. Struct(1,2); works just fine without that constructor being defined.
Right, but if you do define it (in order to do something extra upon initialization -- validate inputs or what have you) then it no longer works at compile time. --bb
This is where CTFE should come to save the day. Andrei
Nov 20 2009
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 20 Nov 2009 00:48:28 -0500, Don <nospam nospam.com> wrote:

 Now that we have struct literals, the old C-style struct initializers  
 don't seem to be necessary.
 The variations with named initializers are not really implemented -- the  
 example in the spec doesn't work, and most uses of them cause compiler  
 segfaults or wrong code generation. EG...

 struct Move{
     int D;
 }
 enum Move genMove = { D:4 };
 immutable Move b = genMove;

 It's not difficult to fix these compiler problems, but I'm just not sure  
 if it's worth implementing. Maybe they should just be dropped? (The {  
 field: value } style anyway).
Brought up in another thread, a good use of static initializers for structs: arrays of POD literals. For example: struct RGB { ubyte red, green, blue; } RGB[256] PALETTE = [ {0x00, 0x00, 0x00}, {0x01, 0x01, 0x01}, ... ]; can you do something like this without static initializers? My recollection is that this is the only way to have a struct array literal. -Steve
Dec 29 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:
 can you do something like this without static initializers?  My  
 recollection is that this is the only way to have a struct array literal.
http://codepad.org/8HnF62s2 Bye, bearophile
Dec 29 2009
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 29 Dec 2009 15:02:50 -0500, bearophile <bearophileHUGS lycos.com>  
wrote:

 Steven Schveighoffer:
 can you do something like this without static initializers?  My
 recollection is that this is the only way to have a struct array  
 literal.
http://codepad.org/8HnF62s2
OK, that makes sense. Last time I remember having issues with CTFE unless the static initializer was used, but that was on D1. -Steve
Dec 29 2009