digitalmars.D.announce - DMD 0.177 release
- Walter Bright (3/3) Dec 09 2006 More ABI changes, and implicit [] => * no longer allowed.
- Rioshin an'Harthen (2/5) Dec 09 2006 Awesome, just awesome... :)
- ideage (2/2) Dec 09 2006 Thank you , Walter! Great D!
- Thomas Kuehne (10/11) Dec 09 2006 -----BEGIN PGP SIGNED MESSAGE-----
- Kyle Furlong (3/8) Dec 09 2006 Good release.
- Endea (6/11) Dec 09 2006 /home/me/D/Derelict/lib/libDerelictGL.a(glx.o):(.gnu.linkonce.d.D47TypeI...
- Thomas Kuehne (11/22) Dec 09 2006 -----BEGIN PGP SIGNED MESSAGE-----
- nazo (7/7) Dec 09 2006 struct is value type so I don't like this change of cast and init.
- Stewart Gordon (9/14) Dec 09 2006 "# Casting a value v to a struct S is now rewritten as S(v).
- Boris Kolar (2/5) Dec 09 2006 Can you please enlighten the rest of us too?
- Lars Ivar Igesund (7/13) Dec 09 2006 S(v) ==> S.opCall(v), that is calling the static opCall of struct S with...
- JohnC (13/16) Dec 09 2006 opAssign is new as well, but it throws an AV - unless I'm not using it
- JohnC (7/25) Dec 09 2006 Take that back. Actually, it does work:
- Lutger (9/25) Dec 09 2006 This works:
- Chris Miller (18/18) Dec 09 2006 Pretty cool stuff.
- Walter Bright (3/5) Dec 09 2006 This wasn't done because there are several use cases where you wouldn't
- Chris Miller (4/9) Dec 09 2006 But if it's optional it's up to the struct writer to use void as the
- Walter Bright (3/16) Dec 09 2006 I don't think that'll work. He'll wind up being forced to set all the
- Stewart Gordon (10/27) Dec 11 2006 Doesn't follow - there might not be any to set other than those that are
- Walter Bright (3/8) Dec 12 2006 But if there *are* other fields that shouldn't be set, a rule to
- Stewart Gordon (7/16) Dec 13 2006 Exactly. *If* there are other fields that shouldn't be set. Do you
- Sean Kelly (38/55) Dec 13 2006 Just a few thoughts. If this change is implemented, it should apply to
- Walter Bright (12/29) Dec 13 2006 opAssign has 3 externally visible characteristics:
- Andrei Alexandrescu (See Website for Email) (8/23) Dec 13 2006 That makes me wonder - if a = b is used without picking up its result
- Walter Bright (5/11) Dec 13 2006 You could have a null pointer passed to the return result, but that
- Andrei Alexandrescu (See Website For Email) (16/34) Dec 13 2006 I guess compiling internally two versions, one that returns S and one
- Walter Bright (4/25) Dec 14 2006 It is possible to force the return type of opAssign. But I'd suggest
- Lionello Lunesu (12/17) Dec 14 2006 class C {
- Walter Bright (4/23) Dec 14 2006 Yes. The issue is that opAssign has both a return value *and* copies
- Andrei Alexandrescu (See Website for Email) (29/36) Dec 14 2006 This is wrong. The assignment should return an lvalue, and the current
- Kevin Bealer (39/56) Dec 14 2006 This reminds me of the "opIndex" discussion we had here quite a while ag...
- Andrei Alexandrescu (See Website For Email) (24/58) Dec 14 2006 Hm. This inability to return lvalues looks like a serious loophole
- Walter Bright (4/8) Dec 14 2006 Ok, I think I understand the issue now. Also, it only applies to struct
- Kevin Bealer (14/22) Dec 15 2006 In the case of opIndex(), I think it does affect classes too. If I do t...
- Kevin Bealer (32/42) Dec 15 2006 ...
- Andrei Alexandrescu (See Website For Email) (47/61) Dec 15 2006 I think it's an lvalue all right. Inside the compiler it's very clear
- Sean Kelly (7/24) Dec 15 2006 Agreed. In my time using D, the only time I've wished for a reference
- Kevin Bealer (61/122) Dec 15 2006 This is really interesting stuff. You've probably seen these already bu...
- Andrei Alexandrescu (See Website For Email) (14/43) Dec 15 2006 [snip]
- Georg Wrede (6/18) Dec 15 2006 LOL!
- Kevin Bealer (90/133) Dec 16 2006 Yes, I have this red covered book around here that goes into more depth,...
- Kevin Bealer (4/9) Dec 19 2006 Did you ever work out how to do this lvalue/rvalue idea?
- Andrei Alexandrescu (See Website for Email) (72/82) Dec 20 2006 I had to leave to Romania before having the time to post thoughts on the...
-
Lionello Lunesu
(11/22)
Dec 20 2006
- Andrei Alexandrescu (See Website for Email) (11/31) Dec 20 2006 Thanks for the kind wishes. One problem I see with the shortcut above is...
- Reiner Pope (8/35) Dec 20 2006 The motivation I see for such a shortcut is that this template pattern
- Andrei Alexandrescu (See Website For Email) (30/50) Dec 20 2006 I'm definitely one for shortcuts, but let's not forget that at the
- Don Clugston (10/14) Dec 20 2006 This off-hand remark worries me. I presume that you mean being able to
- Andrei Alexandrescu (See Website For Email) (28/43) Dec 20 2006 It would indeed break an enormous amount of code, but "all costs"
- Dave (15/63) Dec 20 2006 Are you suggesting either / both:
- Derek Parnell (26/43) Dec 20 2006 On Wed, 20 Dec 2006 06:24:28 -0800, Andrei Alexandrescu (See Website For
- Chris Nicholson-Sauls (20/28) Dec 20 2006 The "[..$]" syntax is also present in ColdC and its relatives (including...
- BCS (12/31) Dec 20 2006 FWIW $ is not only used for the RHS of a slice
- Chris Nicholson-Sauls (14/49) Dec 20 2006 Ack. Having never used anything quite like that before, I guess I had a...
- Frits van Bommel (17/66) Dec 20 2006 These are very possible and are at times very useful. Though the last
- Bill Baxter (13/27) Dec 20 2006 I think that syntax is a little more attractive than ~ for some cases.
- Andrei Alexandrescu (See Website For Email) (115/125) Dec 21 2006 Gladly; I dug my email and let me share a couple of excerpts.
-
Lutger
(6/19)
Dec 21 2006
- Andrei Alexandrescu (See Website for Email) (6/12) Dec 21 2006 The plan is that $expression is rewritten into (expression).length. The
- Pragma (19/33) Dec 21 2006 Nice post, and one heck of an argument!
- Chris Nicholson-Sauls (6/44) Dec 21 2006 If $ is like a 'this', then it ought to be have semantically the same, s...
- Bill Baxter (25/75) Dec 21 2006 In both of those cases the use seems rather silly to me because a and b
- Chris Nicholson-Sauls (9/79) Dec 21 2006 The problem with actually using the 'this' keyword in place of $ is one ...
- Andrei Alexandrescu (See Website For Email) (9/22) Dec 22 2006 This isn't going to be agreeable to most since the purpose of $ in the
- Pragma (7/35) Dec 22 2006 Understood. I just figured I'd throw that out there in case it had any
- Don Clugston (4/16) Dec 21 2006 Provided that some such expansion path for "$" exists, it would seem to
- Andrei Alexandrescu (See Website For Email) (5/22) Dec 22 2006 That is correct. One advantage of the unary/nullary $ is that it's a
- Bill Baxter (26/37) Dec 22 2006 Please give some thought to the case where a and b are of types not
- Bill Baxter (8/25) Dec 21 2006 Slight typo there. Last line should of course have been:
- Andrei Alexandrescu (See Website for Email) (13/32) Dec 22 2006 I did. The thing with language design is that it's easy to either
- Bill Baxter (20/56) Dec 22 2006 Maybe so. Multidimensional arrays seem as common as air from where I
- Bill Baxter (4/16) Dec 24 2006 Wouldn't that ambiguity be fixed by making $ a postfix unary operator
- Bill Baxter (80/114) Dec 20 2006 That hadn't occurred to me, but you're right. I never use length in
- Oskar Linde (32/49) Dec 21 2006 I'd be very interested in looking at what you've come up with. With my
- Bill Baxter (12/50) Dec 24 2006 Yeh, that's similar to what I'm doing too. But it's pretty ugly. So I
- Reiner Pope (8/49) Dec 24 2006 Is there anything particularly wrong with having foo[a..b,c,d..$] being
- Bill Baxter (23/72) Dec 24 2006 Generally speaking, indexing isn't free. And doing it three times is 3x...
- Oskar Linde (8/15) Dec 26 2006 It is a neat idea that unfortunately doesn't work.
- Oskar Linde (16/66) Dec 26 2006 Yes. Apart from the no-argument opSlice and opSliceAssign, that I overlo...
- Bill Baxter (3/65) Dec 26 2006 Sounds great. Is your code available anywhere?
- Kevin Bealer (60/104) Dec 21 2006 I guess the question is, what is the best alternative. I agree about
- Steve Horne (18/21) Dec 22 2006 I've always had a strong feeling that all-lowercase words should be
- Benji Smith (10/21) Dec 20 2006 I don't get it.
- Chris Nicholson-Sauls (6/24) Dec 20 2006 For functions, I essentially agree. There aren't many use cases for it,...
- Andrei Alexandrescu (See Website For Email) (3/7) Dec 21 2006 Perl 5 too.
- Andrei Alexandrescu (See Website For Email) (10/27) Dec 21 2006 Methods might want to return lvalues, but indeed the need is not
- Charles D Hixson (8/36) Dec 21 2006 FWIW,
- Bruno Medeiros (7/35) Dec 24 2006 There have been numerous cases here in the NG of people griping with
- Thomas Kuehne (17/40) Dec 20 2006 -----BEGIN PGP SIGNED MESSAGE-----
- Andrei Alexandrescu (See Website For Email) (7/39) Dec 20 2006 It's a type because the symbol "int" is already bound as a keyword.
- Thomas Kuehne (13/35) Dec 20 2006 -----BEGIN PGP SIGNED MESSAGE-----
- Andrei Alexandrescu (See Website For Email) (4/9) Dec 21 2006 You're right, it makes parsing dependent on the symbol table, breaking a...
- Bruno Medeiros (36/135) Dec 26 2006 Ohh, why didn't you start a new thread... I hate it when a thread gets
- Andrei Alexandrescu (See Website for Email) (5/25) Dec 14 2006 There is no *extra* copying, in the sense that exactly one copy is done....
- Lionello Lunesu (4/29) Dec 15 2006 I'd think that the optimizer would not only eliminate the extra copy,
- Sean Kelly (3/32) Dec 15 2006 If the routine is inlined perhaps, but otherwise not.
- Andrei Alexandrescu (See Website for Email) (13/33) Dec 14 2006 Makes sense, but then I keep on racking my brain to ever think of any
- Kevin Bealer (28/62) Dec 14 2006 I wonder, are there cases where assignment should not return 'this'?
- Sean Kelly (13/28) Dec 14 2006 I've been thinking the same thing, and so far haven't been able to come
- Stewart Gordon (5/25) Dec 14 2006 C'mon, what's your use case for being allowed to return something other
- Walter Bright (6/8) Dec 14 2006 I don't have one. But I prefer to impose as few restrictions as
- Bill Baxter (3/12) Dec 14 2006 For one, it might be useful to return some sort of proxy for 'a'.
- Andrei Alexandrescu (See Website for Email) (3/12) Dec 14 2006 Just did so in my previous post.
- Stewart Gordon (4/13) Dec 14 2006 We already have what strikes the rest of us as a compelling case: what
- Walter Bright (4/6) Dec 14 2006 That case was trying to make opAssign be a constructor. Since there are
- Stewart Gordon (6/13) Dec 15 2006 The cases you seemingly pander to for having opAssign in the first place...
- Chris Miller (4/4) Dec 09 2006 char* p =3D new char[32];
- Alexander Panek (7/14) Dec 09 2006 char *newCharz( uint size ) {
- Chris Miller (8/19) Dec 09 2006 Well, I'm not sure what it should be, but you already made a mistake: it...
- Alexander Panek (9/34) Dec 09 2006 Oi. Sorry:
- Alexander Panek (19/57) Dec 09 2006 Those are newCharz and newChars, of course.
- Chris Miller (8/26) Dec 09 2006 Not bad, perhaps needs better names though. newChars still sounds like n...
- Pragma (7/14) Dec 11 2006 Well, the problem is that since this change, char[] is not the same
- Burton Radons (12/12) Dec 09 2006 - Casting a value v to a struct S is now rewritten as S(v).
- Chris Nicholson-Sauls (6/20) Dec 09 2006 Essentially agreed. I'm not entirely fond of the "silent" new opAssign ...
- Sean Kelly (11/32) Dec 09 2006 Same. And frankly, I'm having trouble coming up with a practical use
- Leopold Walkling (4/41) Dec 09 2006 Why are opAssign overloads possible for structs? They lead to a bad
- John Reimer (4/7) Dec 09 2006 um... but this is the link for dmd.175.zip again, not 177.
- pmoore (1/1) Dec 09 2006 Thanks for this. But std.demangle seems to be broken now.
- David L. Davis (25/28) Dec 09 2006 Walter in dmd v0.176 you improved name mangling, but I've found one very...
- Walter Bright (5/23) Dec 09 2006 That's correct.
- Don Clugston (3/16) Dec 10 2006 Between 0.148 and 0.175, function pointers used 'b' for 'bool', but the
- David L. Davis (10/26) Dec 12 2006 Don thanks for clarifying the reason, I didn't know about the function
- Jarrett Billingsley (20/23) Dec 09 2006 No offense, but would it honestly kill you to allow ctors in structs? W...
- Kirk McDonald (11/45) Dec 09 2006 The thing with opAssign, I realised after reading the spec, is that it
- Walter Bright (18/36) Dec 09 2006 It turns out that static opCall() and this() are equivalent. C++ has
- Jarrett Billingsley (18/38) Dec 09 2006 For classes they're not. Why should structs be any different? And even...
- Walter Bright (11/35) Dec 09 2006 Not perfectly, but the differences are small.
- Burton Radons (13/32) Dec 09 2006 Do you have a single instance of a static opCall being used for
- Walter Bright (4/9) Dec 09 2006 Given:
- Jarrett Billingsley (5/10) Dec 09 2006 Okay, this obviously isn't working. Let's go at this from another angle...
- Walter Bright (6/8) Dec 09 2006 Constructor:
- Burton Radons (41/54) Dec 09 2006 Here's the difference:
- Walter Bright (18/33) Dec 09 2006 struct S
- Lars Ivar Igesund (10/46) Dec 10 2006 Struct can have opCall, which is why they were used as a workaround for
- Chad J (25/63) Dec 11 2006 OK here are the things that make me want constructors instead of static
- Jarrett Billingsley (22/27) Dec 09 2006 In addition to what Burton posted, class:
- Tom S (29/42) Dec 09 2006 The difference is that a ctor initializes an already-existing instance,
- Walter Bright (41/75) Dec 09 2006 Ok, you're right on that one. But I am going to argue on philosophical
-
Don Clugston
(2/10)
Dec 10 2006
Intriguing. Evidently the ideas barrel is far from empty.
-
Walter Bright
(2/3)
Dec 13 2006
No chance of that happening
. - Bill Baxter (46/48) Dec 10 2006 That is a relief to know. What about heap construction? I think
- Kirk McDonald (10/13) Dec 10 2006 One issue I have with allowing struct constructors is that the existence...
- Bill Baxter (22/32) Dec 10 2006 That seems like a non-issue to me. Even if people do take that to mean
- Bill Baxter (5/8) Dec 10 2006 I mean "a side effect of it *not* using unambiguous constructor-like
- Andrei Alexandrescu (See Website For Email) (3/14) Dec 10 2006 Who knows, maybe D will have destructors for structs one day :o).
- Jarrett Billingsley (6/8) Dec 10 2006 "Andrei Alexandrescu (See Website For Email)"
- Jarrett Billingsley (3/6) Dec 10 2006 You can't see it, but there's a ";)" at the end of that post.
- Boris Kolar (5/9) Dec 11 2006 I certainly do :) Since struct types are always known at compile-time,
- BCS (36/44) Dec 11 2006 [...]
- Walter Bright (3/21) Dec 13 2006 Assignment to this inside a constructor is a mistake as it breaks the
- BCS (25/49) Dec 13 2006 What assumptions does it break? This would be valid:
- Walter Bright (2/9) Dec 14 2006
- Tom (9/22) Dec 10 2006 I don't understand: the current way to go (opCall) *is a workaround*.
- Boris Kolar (19/28) Dec 10 2006 Yes, but ctors would work even better:
- Brad Roberts (15/26) Dec 09 2006 Just because someone at some time in the distant pass realized that
- Sean Kelly (14/19) Dec 11 2006 What ever happened to structs as aggregates? I thought this was their
- Andrei Alexandrescu (See Website For Email) (30/51) Dec 11 2006 Classes are different from structs in two essential ways:
- Bruno Medeiros (8/53) Dec 11 2006 "allow controlled overwriting of their state" -> Would that be for the
- Andrei Alexandrescu (See Website for Email) (13/17) Dec 11 2006 That too. The canonical example is simpler - reject invalid attempts at
- Felix T (3/3) Dec 11 2006 To Andrei Alexandrescu:
- Sean Kelly (13/37) Dec 11 2006 Point made. And I suppose opAssign isn't truly linked to the idea of
- Walter Bright (3/10) Dec 12 2006 That's one of the things I felt in my bones, but was unable to put my
- Boris Kolar (30/40) Dec 12 2006 Actually, polymorphism does not imply referential semantics (nor does
- Ivan Senji (3/7) Dec 12 2006 Why not? I think that D could rock the .NET world too. I always wished
- Alexander Panek (9/17) Dec 12 2006 .NET is not the solution for all problems. There will be other library
- Ivan Senji (6/24) Dec 12 2006 I sure hope so.
- Alexander Panek (10/40) Dec 12 2006 Are you, actually, talking about a D -> MSIL compiler, so it can be run
- Ivan Senji (5/24) Dec 12 2006 Actually I really was talking about a D->MSIL compiler for those cases
- Lionello Lunesu (4/8) Dec 13 2006 We had one, once : ( but then the guy's HDD crashed... Sad, sad story.
- Chris Nicholson-Sauls (5/17) Dec 14 2006 https://mywebspace.wisc.edu/daaugustine/web/d/
- Justin C Calvarese (10/30) Dec 14 2006 I think I have pretty much all of the binary files he released (though
- Lionello Lunesu (3/31) Dec 15 2006 Can't assemblies be fairly easily disassembled?
- Sean Kelly (5/36) Dec 15 2006 Yes. In fact, the times I've done it I've seen an exact copy of the
- Andrei Alexandrescu (See Website For Email) (8/36) Dec 12 2006 Of course. It's a "true but uninteresting" fact. Even in current D you
- Andrei Alexandrescu (See Website For Email) (3/16) Dec 12 2006 I feel it in my bones too. It hurts when it rains :o).
- John Reimer (72/90) Dec 11 2006 =
- Sean Kelly (12/44) Dec 11 2006 I think the problem here is that such initializers would not apply to
- John Reimer (13/36) Dec 11 2006 =
- Marcin Kuszczak (44/60) Dec 09 2006 I wonder if it is not possible to make D, working as a base with values ...
- Marcin Kuszczak (30/35) Dec 09 2006 opAssing not mentioned here, but anyway I like what I have read in Docs!
- Hasan Aljudy (10/15) Dec 09 2006 # Casting a value v to a struct S is now rewritten as S(v).
- Jarrett Billingsley (3/5) Dec 09 2006 Have a look on the "Operator Overloading" page of the spec.
- clayasaurus (20/25) Dec 09 2006 I'm not sure if this is a bug or what, but to get derelict from not
- Frank Benoit (keinfarbton) (1/1) Dec 10 2006 did you recompile linked D libs?
- clayasaurus (3/4) Dec 10 2006 Yes, I actually don't use any linked libs since D compiles so fast. I
- Lionello Lunesu (6/6) Dec 10 2006 Walter, you forgot to change this line in the docs:
-
Walter Bright
(1/1)
Dec 10 2006
- Samuel MV (2/2) Dec 10 2006 Thank you, Walter.
- Alexander Panek (3/6) Dec 10 2006 s/general purpose \(language\)/\1 EVER/
- Pragma (14/19) Dec 11 2006 Walter, this is a great update. Especially the removal of the implicit
- Stewart Gordon (7/14) Dec 11 2006 What do you mean by this?
- Pragma (62/79) Dec 12 2006 I'm glad you asked. :)
- Jarrett Billingsley (8/21) Dec 12 2006 Personally for in-place stack allocation, I like the C++ style the best....
- Lutger (10/15) Dec 12 2006 About this optimization business, is this an issue? Since Walter stated
- Jarrett Billingsley (6/15) Dec 12 2006 The impression I get from Walter is that _eeeevery_ compiler has
- Pragma (8/27) Dec 12 2006 Exactly.
- Lutger (4/25) Dec 12 2006 Can you explain why? 'Rely' in this context doesn't mean the language is...
- Jarrett Billingsley (18/21) Dec 12 2006 Yes, I guess that's true. But if a simple addition i.e.
- Frits van Bommel (12/29) Dec 12 2006 Almost. I could see it generate the following code (with optimizations
- Walter Bright (10/13) Dec 12 2006 That problem was solved 15 years ago (for C and C++). I admit that due
- Andrei Alexandrescu (See Website For Email) (3/5) Dec 12 2006 That means you can't return the result in a register :o).
- Sean Kelly (3/8) Dec 12 2006 That talk can stay on comp.lang.c++.moderated, thank you very much ;-)
- Walter Bright (2/7) Dec 13 2006 Actually, it does return it in a register if it fits in one. Try it!
- Jarrett Billingsley (13/18) Dec 13 2006 Alright then. All I've got then is the orthogonality argument. But you...
- Chris Miller (15/37) Dec 13 2006 For the record, I'm fine with static opCall. Structs don't have
- Mark Wrenn (7/7) Dec 13 2006 On Wed, 13 Dec 2006 12:21:37 -0500, Chris Miller wrote:
- Andrei Alexandrescu (See Website For Email) (4/13) Dec 13 2006 If structs do add destructors, this can will be reopened - and by that
- Lionello Lunesu (13/13) Dec 13 2006 Let's not forget that the members of a struct can be initialized "inline...
- Leopold Walkling (17/42) Dec 14 2006 To me another big problem is the cast to a struct. How could anyone, who...
- Burton Radons (38/133) Dec 21 2006 If that's not compiled into a direct write (even to the point of keeping...
- Alexander Panek (14/14) Dec 12 2006 http://digitalmars.com/d/attribute.html#align
- Russ Lewis (3/8) Dec 15 2006 Looks like casts from void* to struct* is broken.
- Russ Lewis (3/14) Dec 21 2006 Not sure what I did wrong or what I'm doing right now, but they seem to
More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zip
Dec 09 2006
"Walter Bright" <newshound digitalmars.com> wrote:More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zipAwesome, just awesome... :)
Dec 09 2006
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Walter Bright schrieb am 2006-12-09:More ABI changes, and implicit [] => * no longer allowed.Thanks, runtime reflection seems to be within reach now. Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFFeqR3LK5blCcjpWoRAuZpAJ4mS+QO5idDF7tKA9nw5d8aZ0xipwCffTYp hFGtpTzRynIPfEhg4qJam48= =EAfw -----END PGP SIGNATURE-----
Dec 09 2006
Walter Bright wrote:More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zipGood release. http://www.digitalmars.com/d/type.html is broken at the moment.
Dec 09 2006
Walter Bright kirjoitti:More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zip/home/me/D/Derelict/lib/libDerelictGL.a(glx.o):(.gnu.linkonce.d.D47TypeInfo_S8derelict6opengl3glx15__GLXconte tRec6__initZ+0x14): undefined reference to `D8derelict6opengl3glx15__GLXcontextRec6__initZ' glx.d does not define the struct, only it's name: struct __GLXcontextRec; So back to .176 for a while...
Dec 09 2006
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Endea schrieb am 2006-12-09:Walter Bright kirjoitti:The linux build of DMD seems to be a mix between version 0.176 and 0.177. Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFFesI+LK5blCcjpWoRAg+SAJsFITmQ0XjTOcSsDnxYaZIAICCgRgCfQvc5 kO1v0ikRMfz47MbJSw2P4lY= =Vbly -----END PGP SIGNATURE-----More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zip/home/me/D/Derelict/lib/libDerelictGL.a(glx.o):(.gnu.linkonce.d.D47TypeInfo_S8derelict6opengl3glx15__GLXconte tRec6__initZ+0x14): undefined reference to `D8derelict6opengl3glx15__GLXcontextRec6__initZ' glx.d does not define the struct, only it's name: struct __GLXcontextRec; So back to .176 for a while...
Dec 09 2006
struct is value type so I don't like this change of cast and init. I think that should introduce tuple literal as {} and support cast from tuple to struct like: struct S{int x,y;} S* s = &cast(S){0,0};//tuple literal + cast *s = cast(S){0,0};//tuple literal + cast #sorry for my poor English.
Dec 09 2006
Walter Bright wrote:More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zipIt took me a moment to realise what you meant by this. This appears to be the latest answer to the multiple opCast problem, albeit only for when the target type is a struct. WIHYE could we please have at least an answer about this soon? http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.bugs&article_id=9360 Stewart.
Dec 09 2006
== Quote from Stewart Gordon (smjg_1998 yahoo.com)'s articleIt took me a moment to realise what you meant by this.Can you please enlighten the rest of us too?
Dec 09 2006
Boris Kolar wrote:== Quote from Stewart Gordon (smjg_1998 yahoo.com)'s articleS(v) ==> S.opCall(v), that is calling the static opCall of struct S with v as an argument. -- Lars Ivar Igesund blog at http://larsivi.net DSource & #D: larsiviIt took me a moment to realise what you meant by this.Can you please enlighten the rest of us too?
Dec 09 2006
"Walter Bright" <newshound digitalmars.com> wrote in message news:ele2k9$2hr5$1 digitaldaemon.com...More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zipopAssign is new as well, but it throws an AV - unless I'm not using it right? class Test { int value; void opAssign(int value) { this.value = value; } } void main() { Test t = 10; }
Dec 09 2006
"JohnC" <johnch_atms hotmail.com> wrote in message news:elef3s$30jn$1 digitaldaemon.com..."Walter Bright" <newshound digitalmars.com> wrote in message news:ele2k9$2hr5$1 digitaldaemon.com...Take that back. Actually, it does work: Test t = new Test; t = 10; Sadly it doesn't like a struct as the lvalue. There are no examples in the docs, so how it's supposed to be used is anyone's guess...More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zipopAssign is new as well, but it throws an AV - unless I'm not using it right? class Test { int value; void opAssign(int value) { this.value = value; } } void main() { Test t = 10; }
Dec 09 2006
JohnC wrote: > opAssign is new as well, but it throws an AV - unless I'm not using itright? class Test { int value; void opAssign(int value) { this.value = value; } } void main() { Test t = 10; }This works: void main() { Test t = new Test; t = 10; } I'm surprised this overload is in, as I thought it was considered dangerous. Not that I'm complaining btw!
Dec 09 2006
Pretty cool stuff. Suggestion: Allow static opAssign to return an instance of the class that will be = assigned to the lvalue: class MyClass { this(int x) { } static MyClass opAssign(int x) { return new MyClass(x); } } // ... MyClass mc; mc =3D 3; // keeps mc null. It seems logical to implement this proposal because to get the desired = effect right now you could do: mc =3D mc =3D 3;
Dec 09 2006
Chris Miller wrote:Allow static opAssign to return an instance of the class that will be assigned to the lvalue:This wasn't done because there are several use cases where you wouldn't want to erase all previous contents of the struct being assigned to.
Dec 09 2006
On Sat, 09 Dec 2006 13:59:24 -0500, Walter Bright <newshound digitalmars.com> wrote:Chris Miller wrote:But if it's optional it's up to the struct writer to use void as the return or not.Allow static opAssign to return an instance of the class that will be assigned to the lvalue:This wasn't done because there are several use cases where you wouldn't want to erase all previous contents of the struct being assigned to.
Dec 09 2006
Chris Miller wrote:On Sat, 09 Dec 2006 13:59:24 -0500, Walter Bright <newshound digitalmars.com> wrote:I don't think that'll work. He'll wind up being forced to set all the fields.Chris Miller wrote:But if it's optional it's up to the struct writer to use void as the return or not.Allow static opAssign to return an instance of the class that will be assigned to the lvalue:This wasn't done because there are several use cases where you wouldn't want to erase all previous contents of the struct being assigned to.
Dec 09 2006
Walter Bright wrote:Chris Miller wrote:Doesn't follow - there might not be any to set other than those that are an inherent part of the assignment operation. For example, consider an immutable big integer class. One might want Int x; ... x = 42; as syntactic sugar for x = new Int(42); Stewart.On Sat, 09 Dec 2006 13:59:24 -0500, Walter Bright <newshound digitalmars.com> wrote:I don't think that'll work. He'll wind up being forced to set all the fields.Chris Miller wrote:But if it's optional it's up to the struct writer to use void as the return or not.Allow static opAssign to return an instance of the class that will be assigned to the lvalue:This wasn't done because there are several use cases where you wouldn't want to erase all previous contents of the struct being assigned to.
Dec 11 2006
Stewart Gordon wrote:Walter Bright wrote:But if there *are* other fields that shouldn't be set, a rule to preclude those cases would be a problem.I don't think that'll work. He'll wind up being forced to set all the fields.Doesn't follow - there might not be any to set other than those that are an inherent part of the assignment operation.
Dec 12 2006
Walter Bright wrote:Stewart Gordon wrote:Exactly. *If* there are other fields that shouldn't be set. Do you even understand a word of what Chris is proposing? The programmer would have a choice - opAssign returning void to modify in-place the object referenced by the lvalue, or returning a new object that will be assigned to the lvalue. What is this precluding? Stewart.Walter Bright wrote:But if there *are* other fields that shouldn't be set, a rule to preclude those cases would be a problem.I don't think that'll work. He'll wind up being forced to set all the fields.Doesn't follow - there might not be any to set other than those that are an inherent part of the assignment operation.
Dec 13 2006
Stewart Gordon wrote:Walter Bright wrote:Just a few thoughts. If this change is implemented, it should apply to all assign ops, not just opAssign itself. So opAddAssign, opMulAssign, etc. Also, it seems like this could give rise to some interesting behavior: class MyClass { int val; this( int x ) { val = x; } MyClass opAssign( int x ) { return new MyClass( x ); } } void fn( MyClass c ) { c = 42; } MyClass c = new MyClass( 0 ); fn( c ); writefln( c.val ); Based on this suggestion, the above will print '0', not '42'. This is actually kind of an interesting situation, as it offers the possibility of making non-inout reference parameters immutable with respect to assign operations. The other consequence being that assign operations could change the address of a class reference as a side-effect, which may have an impact on optimization. It also may interact somewhat oddly with associative arrays and such, depending on how the compiler generates code. ie. MyClass[int] aa; aa[0] = new MyClass( 0 ); aa[0] = 42; writefln( aa[0].val ); What will the above print? This sort of behavior would need to be defined in the spec. SeanStewart Gordon wrote:Exactly. *If* there are other fields that shouldn't be set. Do you even understand a word of what Chris is proposing? The programmer would have a choice - opAssign returning void to modify in-place the object referenced by the lvalue, or returning a new object that will be assigned to the lvalue. What is this precluding?Walter Bright wrote:But if there *are* other fields that shouldn't be set, a rule to preclude those cases would be a problem.I don't think that'll work. He'll wind up being forced to set all the fields.Doesn't follow - there might not be any to set other than those that are an inherent part of the assignment operation.
Dec 13 2006
Stewart Gordon wrote:Walter Bright wrote:opAssign has 3 externally visible characteristics: 1) the parameter 2) the 'this' pointer 3) the return value Your proposal mixes up 2 and 3. opAssign works like: a = b becomes: a.opAssign(b) The return value is not assigned to a, it is the value of the expression (a = b). Mixing up the return value and the assignment to a will cause problems, as the two are different things, and should be independent.Stewart Gordon wrote:Exactly. *If* there are other fields that shouldn't be set. Do you even understand a word of what Chris is proposing? The programmer would have a choice - opAssign returning void to modify in-place the object referenced by the lvalue, or returning a new object that will be assigned to the lvalue. What is this precluding?Walter Bright wrote:But if there *are* other fields that shouldn't be set, a rule to preclude those cases would be a problem.I don't think that'll work. He'll wind up being forced to set all the fields.Doesn't follow - there might not be any to set other than those that are an inherent part of the assignment operation.
Dec 13 2006
Walter Bright wrote:opAssign has 3 externally visible characteristics: 1) the parameter 2) the 'this' pointer 3) the return value Your proposal mixes up 2 and 3. opAssign works like: a = b becomes: a.opAssign(b) The return value is not assigned to a, it is the value of the expression (a = b). Mixing up the return value and the assignment to a will cause problems, as the two are different things, and should be independent.That makes me wonder - if a = b is used without picking up its result (the usual case), and if opAssign() returns an S, will the compiler optimize away the extra copy at zero cost? What I think happens is that the function will take a pointer to the destination which is zero, so a comparison will be made. But perhaps the optimizer will take care of eliminating that? Andrei
Dec 13 2006
Andrei Alexandrescu (See Website for Email) wrote:That makes me wonder - if a = b is used without picking up its result (the usual case), and if opAssign() returns an S, will the compiler optimize away the extra copy at zero cost?No. The caller and callee don't know about each other.What I think happens is that the function will take a pointer to the destination which is zero, so a comparison will be made. But perhaps the optimizer will take care of eliminating that?You could have a null pointer passed to the return result, but that would entail an extra check on the part of the callee. This is why a lot of C++ code tends to return references instead of values.
Dec 13 2006
Walter Bright wrote:Andrei Alexandrescu (See Website for Email) wrote:I guess compiling internally two versions, one that returns S and one that returns void, might be worth looking into. Because right now user code for opAssign() can't be politically correct and efficient at the same time. Here's a better alternative: Require opAssign() to always return void and have the compiler return a reference to the left-hand side. That is, transform: a = b; into: (a.opAssign(b), a); with the mention that a only gets evaluated once. This way assignment _always_ returns its left-hand side and user code cannot subvert that behavior. Also the code will be efficient because no more spurious copies are being made. AndreiThat makes me wonder - if a = b is used without picking up its result (the usual case), and if opAssign() returns an S, will the compiler optimize away the extra copy at zero cost?No. The caller and callee don't know about each other.What I think happens is that the function will take a pointer to the destination which is zero, so a comparison will be made. But perhaps the optimizer will take care of eliminating that?You could have a null pointer passed to the return result, but that would entail an extra check on the part of the callee. This is why a lot of C++ code tends to return references instead of values.
Dec 13 2006
Andrei Alexandrescu (See Website For Email) wrote:I guess compiling internally two versions, one that returns S and one that returns void, might be worth looking into. Because right now user code for opAssign() can't be politically correct and efficient at the same time. Here's a better alternative: Require opAssign() to always return void and have the compiler return a reference to the left-hand side. That is, transform: a = b; into: (a.opAssign(b), a); with the mention that a only gets evaluated once. This way assignment _always_ returns its left-hand side and user code cannot subvert that behavior. Also the code will be efficient because no more spurious copies are being made.It is possible to force the return type of opAssign. But I'd suggest making it an S*, with the rewrite to: *(a.opAssign(b))
Dec 14 2006
Walter Bright wrote:It is possible to force the return type of opAssign. But I'd suggest making it an S*, with the rewrite to: *(a.opAssign(b))class C { C* opAssign(...) {...} } struct S { S* opAssign(...) {...} } Seriously?? Ugh. Why not just "C opAssign" and "S opAssign"? In the opCall discussion you said yourself that the return value will be optimized. Am I missing something? L.
Dec 14 2006
Lionello Lunesu wrote:Walter Bright wrote:It isn't necessary for classes, as they are already reference types.It is possible to force the return type of opAssign. But I'd suggest making it an S*, with the rewrite to: *(a.opAssign(b))class C { C* opAssign(...) {...} } struct S { S* opAssign(...) {...} } Seriously?? Ugh.Why not just "C opAssign" and "S opAssign"? In the opCall discussion you said yourself that the return value will be optimized. Am I missing something?Yes. The issue is that opAssign has both a return value *and* copies values into it's 'this' pointer.
Dec 14 2006
Walter Bright wrote:This is wrong. The assignment should return an lvalue, and the current semantics of opAssign do not allow that. Here is some code: void Increment(inout int x) { ++x; } int a; Increment(a = 5); This code leaves a containing the value 6. This is because a is first assigned a 5, then a is passed __as an lvalue__ to Increment, which bumps it. Now consider: struct S { int a; S opAssign(int x) { a = x; return this; } } void Increment(inout S x) { ++x.a; } S a; Increment(a = 5); This is going to have very different (and useless and unwanted) semantics. Again, the right thing to do: give the Caesar what belongs to the Caesar. Have the user do the assignment (and return void), and have the compiler pass the lhs lvalue around, when needed. AndreiWhy not just "C opAssign" and "S opAssign"? In the opCall discussion you said yourself that the return value will be optimized. Am I missing something?Yes. The issue is that opAssign has both a return value *and* copies values into it's 'this' pointer.
Dec 14 2006
== Quote from Andrei Alexandrescu (See Website for Email) > Now consider:struct S { int a; S opAssign(int x) { a = x; return this; } } void Increment(inout S x) { ++x.a; } S a; Increment(a = 5); This is going to have very different (and useless and unwanted) semantics. Again, the right thing to do: give the Caesar what belongs to the Caesar. Have the user do the assignment (and return void), and have the compiler pass the lhs lvalue around, when needed. AndreiThis reminds me of the "opIndex" discussion we had here quite a while ago. The problem was that for user defined containers, you can't simulate X[i] if the contained type is something like a struct. class C { S s1; S opIndex(int i) { return s1; } } There is an opIndexAssign(), but it has the same problem when applied to the inout parameter. From the discussion at the time (Ben Hinkle might remember) I recall two possibilities that seemed to make sense to me. 1. Add a return type qualifier with "inout" semantics, like a C++ "&" type. Something like one of these, inout of course looks a bit funny: ref S opIndexRef(int i); inout S opIndexRef(int i); 2. Return S* and let the compiler apply the "*". S* opIndex(int i); Compiler silently transforms foo(x[i]) into foo(*x[i]) A* A::opAssign(inout A b); S* A::opIndexAssign(int i); assign1: a.opAssign(b) -> (a.opAssign(b), a) assign2: a.opAssign(b) -> *(a.opAssign(b)) index1: a.opIndexAssign(i) -> *opIndexAssign(i) Where index1 is my opIndex suggestion (from way back) and assign1 and assign2 are Walter and Andrei's possible syntaxes for opAssign() respectively. Conceptually, the problem looks similar to me: how to "tunnel" an assignable type through a return value of a method, like the C++ "&" types do. The opIndex version needs to tunnel up through more layers than opAssign() does since it's not the A type, so the (a.opIndex(b), a) can't work there of course. I would vote for "S*" being the conceptual return type in both cases for opAssign() and opIndex(), although in the case of opAssign() it wouldn't bother me if the user visible signature was "void opAssign(...)" and the compiler handles the pointer as it does with this(). Kevin
Dec 14 2006
Kevin Bealer wrote:This reminds me of the "opIndex" discussion we had here quite a while ago. The problem was that for user defined containers, you can't simulate X[i] if the contained type is something like a struct. class C { S s1; S opIndex(int i) { return s1; } } There is an opIndexAssign(), but it has the same problem when applied to the inout parameter.Hm. This inability to return lvalues looks like a serious loophole within D's type system.1. Add a return type qualifier with "inout" semantics, like a C++ "&" type. Something like one of these, inout of course looks a bit funny: ref S opIndexRef(int i); inout S opIndexRef(int i);This will have serious ripples through the type system. For example, people might start to ask themselves whether they can define standalone inout variables, and what that means. Or, tell inside a template whether it received an inout parameter or not. At any rate, Perl 5 has implemented this exact hack, and seems to be working with it. See: http://search.cpan.org/~nwclark/perl-5.8.7/pod/perlsub.pod#Lvalue_subroutines The Perl guys seem to be unhappy about it because they labeled it as "experimental" in Perl 5 and they discuss eliminating it in Perl 6, in favor of some even more exotic features. See: http://dev.perl.org/perl6/rfc/149.html2. Return S* and let the compiler apply the "*". S* opIndex(int i); Compiler silently transforms foo(x[i]) into foo(*x[i])This is also (probably more) unsatisfactory. People will be able to index and obtain lvalues, but will be unable to return something assignable from a function: ++s[a]; // possible through a compiler hack ++s(a, b); // impossibleConceptually, the problem looks similar to me: how to "tunnel" an assignable type through a return value of a method, like the C++ "&" types do. The opIndex version needs to tunnel up through more layers than opAssign() does since it's not the A type, so the (a.opIndex(b), a) can't work there of course. I would vote for "S*" being the conceptual return type in both cases for opAssign() and opIndex(), although in the case of opAssign() it wouldn't bother me if the user visible signature was "void opAssign(...)" and the compiler handles the pointer as it does with this().returning inout is better, but I foresee a bunch of issues with it. The question whether lvalues are needed beyond function return values must be satisfactorily answered. Andrei
Dec 14 2006
Andrei Alexandrescu (See Website For Email) wrote:returning inout is better, but I foresee a bunch of issues with it. The question whether lvalues are needed beyond function return values must be satisfactorily answered.Ok, I think I understand the issue now. Also, it only applies to struct types, not class types. Class types are already reference types, so for them it's a non-issue.
Dec 14 2006
== Quote from Walter Bright (newshound digitalmars.com)'s articleAndrei Alexandrescu (See Website For Email) wrote:In the case of opIndex(), I think it does affect classes too. If I do this: void Swap(inout T a, inout T b) { T c=a; a=b; b=c; } class Vector { ... }; Vector X; 1. X[i].increment(); 2. Swap(X[i], X[j]); Kevinreturning inout is better, but I foresee a bunch of issues with it. The question whether lvalues are needed beyond function return values must be satisfactorily answered.Ok, I think I understand the issue now. Also, it only applies to struct types, not class types. Class types are already reference types, so for them it's a non-issue.
Dec 15 2006
== Quote from Andrei Alexandrescu (See Website For Email) (SeeWebsiteForEmail erdani.org)'s articleKevin Bealer wrote:...I missed this possibility. In order to solve just this next step { ++s(a, b); }, it seems like D would need something that copies like a pointer to either a struct or class reference, but automatically turns into (or acts like) a regular reference or LValue when something like ++ is applied. This means something in between a pointer and an LValue. I'll keep calling it 'ref' for now. I think to get the semantics we'd expect it needs these properties: 1. For struct, it can be returned from a method/function or passed as an (inout) argument one or more times without causing any struct-copy to happen, and still referring to the original class reference (not just the original class). 2. For class, it can be used to modify the original object reference itself, i.e. for: ref C X::opIndex(int i); foo(inout C); foo(x[i]) should have the potential to modify the reference in the i'th location of x, by replacing with a different C object. 3. It should autoconvert to (or behave like) a regular LValue when something like ++ or += happens. Of course if += is called and opAddAssign() returns "ref T", then the returned reference would propagate a ref to the same original entity. perfectly okay). Or assignment to the ref itself (the assign and other operations would by applied to the referred object of course). It's also probably convertable to a pointer via "&", i.e. address-of returns a pointer-to-original instead of pointer-to-ref. I guess this means it's impossible to get an address of the "ref" type itself except via trickery? I kind of hate to say it, but given all this, how far am I from describing a C++ "&" type? Is there anything the C++ type does that isn't in the above list? (Other than the new proposal for '& &' references I guess, which seems unnecessary for D.) Kevin2. Return S* and let the compiler apply the "*". S* opIndex(int i); Compiler silently transforms foo(x[i]) into foo(*x[i])This is also (probably more) unsatisfactory. People will be able to index and obtain lvalues, but will be unable to return something assignable from a function: ++s[a]; // possible through a compiler hack ++s(a, b); // impossible
Dec 15 2006
Kevin Bealer wrote:== Quote from Andrei Alexandrescu (See Website For Email) (SeeWebsiteForEmail erdani.org)'s articleI think it's an lvalue all right. Inside the compiler it's very clear what's an lvalue and what's not. Think of this: int x; Wherever you use x, even if you pass it to functions etc., the compiler entirely knows it's an lvalue. If, on the other hand, you use (x + 1), all of a sudden the compiler understands that's an rvalue, and accordingly won't let you e.g. pass it as an out or inout parameter. I was quite worried last night about this issue, but now I think I realize that returning from a function is about the only (I hope!) case in which the lvalue yes/no information about a value is lost. That is, you can't today write a function ident() that is totally transparent - i.e., can be added around any expression with no effect whatsoever: template ident(T) { T ident(T rvalue) { // works return rvalue; } T ident(inout T lvalue) { // loss of information return lvalue; } } By the way, ident() is a good function to check a language's quality. If it can't be implemented at all or efficiently, then the language is not ideal. Most languages can implement ident() but not efficiently. C++ came close to implementing it properly, but all the confusion between rvalues and const references made implementation exceedingly difficult (to add insult to injury, built-in rvalues are treated differently than user-defined rvalues). Current D cannot implement ident at all. I also wonder whether overloading on inout is possible. I think not, and if not, that is a good urgent item to put on the list. The code that should compile and execute correctly is: template ident(T) { T ident(T rvalue) { // works return rvalue; } inout T ident(inout T lvalue) { // works return lvalue; } }++s[a]; // possible through a compiler hack ++s(a, b); // impossibleI missed this possibility. In order to solve just this next step { ++s(a, b); }, it seems like D would need something that copies like a pointer to either a struct or class reference, but automatically turns into (or acts like) a regular reference or LValue when something like ++ is applied. This means something in between a pointer and an LValue.I kind of hate to say it, but given all this, how far am I from describing a C++ "&" type? Is there anything the C++ type does that isn't in the above list? (Other than the new proposal for '& &' references I guess, which seems unnecessary for D.)There are a few differences that make all the difference :o). One is that C++ is too eager to convert rvalues to const &, which has caused an unbounded amount of harm. Second, in C++, T& is a type indeed, but it's a half-life type, a pariah. So it would be probably wise to do the entire lvalue/rvalue distinction without making references into types. For example, outside function declarations, there should be no other place where inout can be used. Andrei
Dec 15 2006
Andrei Alexandrescu (See Website For Email) wrote:Kevin Bealer wrote:Agreed. In my time using D, the only time I've wished for a reference type is for function return values. The language doesn't need a general reference qualifier. Besides, we've already got inout parameter types, and it seems completely reasonable that one should be able to pass references out of a function as well as into a function. SeanI kind of hate to say it, but given all this, how far am I from describing a C++ "&" type? Is there anything the C++ type does that isn't in the above list? (Other than the new proposal for '& &' references I guess, which seems unnecessary for D.)There are a few differences that make all the difference :o). One is that C++ is too eager to convert rvalues to const &, which has caused an unbounded amount of harm. Second, in C++, T& is a type indeed, but it's a half-life type, a pariah. So it would be probably wise to do the entire lvalue/rvalue distinction without making references into types. For example, outside function declarations, there should be no other place where inout can be used.
Dec 15 2006
== Quote from Andrei Alexandrescu (See Website For Email) (SeeWebsiteForEmail erdani.org)'s articleKevin Bealer wrote:This is really interesting stuff. You've probably seen these already but lately D has gotten some compile time introspection stuff via tuples, in particular I'm thinking of (http://www.digitalmars.com/d/tuple.html) and (http://www.digitalmars.com/d/phobos/std_traits.html), which I think look quite useful. So I'm imagining a case of "meta-ident", which is to say, a template cousin of the ident test, applying the 'wrapper' concept to metaprogramming instead of programming. Knowing only the name of a method of a parameter class method (T::run), I want to duplicate it's signature in a template class method (A::meta). template A(T) { class A { T wrapped; this(T w) { wrapped = w; } ReturnType!(T.run) meta1(ParameterType!(T.run) i) { return wrapped.run(i); } inout ReturnType!(T.run) meta2(inout ParameterType!(T.run) i) { return wrapped.run(i); } } } Looking just at meta1, I'm wondering if it can absorbs the LValue-ness (inout qualification) through the Tuple. I'm thinking that it can't. If the LValue return type concept only affects function signatures as you say, then it can't be stored in a tuple, right? Instead it would get the type minus the inout qualifier? Now consider these two definitions of T::run(): struct S {...} S T::run1(in S x); inout S T::run2(inout S x); If I want to write a perfect-forwarding wrapper for run2, I need to use something like meta2, so that the LValue-ness is preserved. But meta2 can't work with run1, because the local S returned by run1 can't be returned via an inout return value. It would be returning a ref to a local var. So I need to know if the function takes and returns out vs. inout, in order to wrap it up like meta() is trying to do. This looks a bit like the C++ problem of having to define const and non-const versions of all the operators, except that meta1 or meta2 will do the wrong thing *silently* in these cases, either returning references to local vars (meta2 + run1), or discarding LValue-ness (meta1 + run2). Maybe I'm solving a non-problem here: in most cases the user should know if T::run() returns an LValue or RValue; for example, opIndex() could be understood to always return an LValue by convention. It seems like this local case could be solved by making ReturnType!() and ParameterTypeTuple!() into language attributes, so that they could see the fully qualified function-signature grade types with passing conventions etc. T::run.return_type_of meta_all(T::run.parameter_type_of[0..$] x) { // pass all the parameters via some kind of compiler-aware tuple return wrapped.run(x[0..$]); } Which could presumably, provide perfect forwarding of T::run() when used in a function signature context. [Or maybe there's always a conceptual 'leak' in these kinds of systems, and it can only be pushed a finite distance toward obscurity with each new language feature. I hope not, but...] Kevin== Quote from Andrei Alexandrescu (See Website For Email) (SeeWebsiteForEmail erdani.org)'s articleI think it's an lvalue all right. Inside the compiler it's very clear what's an lvalue and what's not. Think of this: int x; Wherever you use x, even if you pass it to functions etc., the compiler entirely knows it's an lvalue. If, on the other hand, you use (x + 1), all of a sudden the compiler understands that's an rvalue, and accordingly won't let you e.g. pass it as an out or inout parameter. I was quite worried last night about this issue, but now I think I realize that returning from a function is about the only (I hope!) case in which the lvalue yes/no information about a value is lost. That is, you can't today write a function ident() that is totally transparent - i.e., can be added around any expression with no effect whatsoever: template ident(T) { T ident(T rvalue) { // works return rvalue; } T ident(inout T lvalue) { // loss of information return lvalue; } } By the way, ident() is a good function to check a language's quality. If it can't be implemented at all or efficiently, then the language is not ideal. Most languages can implement ident() but not efficiently. C++ came close to implementing it properly, but all the confusion between rvalues and const references made implementation exceedingly difficult (to add insult to injury, built-in rvalues are treated differently than user-defined rvalues). Current D cannot implement ident at all. I also wonder whether overloading on inout is possible. I think not, and if not, that is a good urgent item to put on the list. The code that should compile and execute correctly is: template ident(T) { T ident(T rvalue) { // works return rvalue; } inout T ident(inout T lvalue) { // works return lvalue; } }++s[a]; // possible through a compiler hack ++s(a, b); // impossibleI missed this possibility. In order to solve just this next step { ++s(a, b); }, it seems like D would need something that copies like a pointer to either a struct or class reference, but automatically turns into (or acts like) a regular reference or LValue when something like ++ is applied. This means something in between a pointer and an LValue.I kind of hate to say it, but given all this, how far am I from describing a C++ "&" type? Is there anything the C++ type does that isn't in the above list? (Other than the new proposal for '& &' references I guess, which seems unnecessary for D.)There are a few differences that make all the difference :o). One is that C++ is too eager to convert rvalues to const &, which has caused an unbounded amount of harm. Second, in C++, T& is a type indeed, but it's a half-life type, a pariah. So it would be probably wise to do the entire lvalue/rvalue distinction without making references into types. For example, outside function declarations, there should be no other place where inout can be used. Andrei
Dec 15 2006
Kevin Bealer wrote:This is really interesting stuff. You've probably seen these already but lately D has gotten some compile time introspection stuff via tuples, in particular I'm thinking of (http://www.digitalmars.com/d/tuple.html) and (http://www.digitalmars.com/d/phobos/std_traits.html), which I think look quite useful.I'm familiar with those :o).So I'm imagining a case of "meta-ident", which is to say, a template cousin of the ident test, applying the 'wrapper' concept to metaprogramming instead of programming. Knowing only the name of a method of a parameter class method (T::run), I want to duplicate it's signature in a template class method (A::meta).[snip]Looking just at meta1, I'm wondering if it can absorbs the LValue-ness (inout qualification) through the Tuple. I'm thinking that it can't. If the LValue return type concept only affects function signatures as you say, then it can't be stored in a tuple, right? Instead it would get the type minus the inout qualifier?Your observation is correct. It all boils down to this: if "inout" is not part of the type system, there are two issues: (1) two overloads will always be needed to catch it (the similar way it's needed to overload the same function on const/non-const), and also (2) compile-time introspection would be more complicated as you need to "catch" the inout part of the parameter separately - the type won't be part of it.It seems like this local case could be solved by making ReturnType!() and ParameterTypeTuple!() into language attributes, so that they could see the fully qualified function-signature grade types with passing conventions etc. T::run.return_type_of meta_all(T::run.parameter_type_of[0..$] x) { // pass all the parameters via some kind of compiler-aware tuple return wrapped.run(x[0..$]); } Which could presumably, provide perfect forwarding of T::run() when used in a function signature context. [Or maybe there's always a conceptual 'leak' in these kinds of systems, and it can only be pushed a finite distance toward obscurity with each new language feature. I hope not, but...]That remains to be seen, but I think the buck stops at functions. The problem of duplicating templates for inout (lvalues) and rvalues stays, but I have an idea about that, that I might tell about tomorrow. Andrei
Dec 15 2006
Andrei Alexandrescu (See Website For Email) wrote:Kevin Bealer wrote:LOL! Incidentally, the technical expertise, theoretical depth, and rigor of thinking that you've brought with you, have been sorely missed here. We'd be nowhere near this far without your already big contributions to D! I can hardly imagine what we've got ahead of us!This is really interesting stuff. You've probably seen these already but lately D has gotten some compile time introspection stuff via tuples, in particular I'm thinking of (http://www.digitalmars.com/d/tuple.html) and (http://www.digitalmars.com/d/phobos/std_traits.html), which I think look quite useful.I'm familiar with those :o).
Dec 15 2006
== Quote from Andrei Alexandrescu (See Website For Email) (SeeWebsiteForEmail erdani.org)'s articleKevin Bealer wrote:Yes, I have this red covered book around here that goes into more depth, but I can never find it... :)This is really interesting stuff. You've probably seen these already but lately D has gotten some compile time introspection stuff via tuples, in particular I'm thinking of (http://www.digitalmars.com/d/tuple.html) and (http://www.digitalmars.com/d/phobos/std_traits.html), which I think look quite useful.I'm familiar with those :o).qualifier?So I'm imagining a case of "meta-ident", which is to say, a template cousin of the ident test, applying the 'wrapper' concept to metaprogramming instead of programming. Knowing only the name of a method of a parameter class method (T::run), I want to duplicate it's signature in a template class method (A::meta).[snip]Looking just at meta1, I'm wondering if it can absorbs the LValue-ness (inout qualification) through the Tuple. I'm thinking that it can't. If the LValue return type concept only affects function signatures as you say, then it can't be stored in a tuple, right? Instead it would get the type minus the inoutYour observation is correct. It all boils down to this: if "inout" is not part of the type system, there are two issues: (1) two overloads will always be needed to catch it (the similar way it's needed to overload the same function on const/non-const), and also (2) compile-time introspection would be more complicated as you need to "catch" the inout part of the parameter separately - the type won't be part of it.That hint got me thinking and I had a look for myself at whether the current system can be used to make the distinction between in and inout. It took me a while to nail down a syntax that could do it. Of course, I can't test it with "inout" return types since we don't have those to play with, but I think what I have here might work for them if they were available. (Maybe inout is a bad name for a return type, but...) This is what I was able to make: import std.stdio; import std.traits; class Refer { alias int Item; Item run(inout Item x) { writefln("run(io)"); x += 300; return 10; } }; class Value { alias int Item; Item run(Item x) { writefln("run(i)"); x += 400; return 20; } }; template Baz(T) { class Baz { T wr_; this() { wr_ = new T(); } alias T.Item Item; alias Item delegate(inout Item) run_IO; alias Item delegate(Item) run_I; alias typeof(& (new T).run) TRunFunc; static if (is(typeof(TRunFunc) == typeof(run_IO))) { Item walk(inout Item x) { writefln("walk(io)"); x += 5000; return wr_.run(x); } } static if (is(typeof(TRunFunc) == typeof(run_I))) { Item walk(Item x) { writefln("walk(i)"); x += 6000; return wr_.run(x); } } } } int main(char[][] args) { alias Baz!(Value) TValue; alias Baz!(Refer) TRefer; TValue v = new TValue; TRefer r = new TRefer; int a = 0; int b = v.walk(a); int c = 0; int d = r.walk(c); writefln("\nValue semantics %s, %s.", a, b); writefln("Refer semantics %s, %s.", c, d); return 0; } Output looks like: walk(i) run(i) walk(io) run(io) Value semantics 0, 20. Refer semantics 5300, 10. So, I can match and select the preferred signature via delegates and typeof etc. Of course, if everyone uses this, it would be good to have some kind of shortcut for the above syntax. Maybe the IsExpression could be expanded to accept types like this: is(T.run() : int function(inout int)) Right now this is accepted (compiles) but does not seem to work - it always seems to evaluate as false. KevinIt seems like this local case could be solved by making ReturnType!() and ParameterTypeTuple!() into language attributes, so that they could see the fully qualified function-signature grade types with passing conventions etc. T::run.return_type_of meta_all(T::run.parameter_type_of[0..$] x) { // pass all the parameters via some kind of compiler-aware tuple return wrapped.run(x[0..$]); } Which could presumably, provide perfect forwarding of T::run() when used in a function signature context. [Or maybe there's always a conceptual 'leak' in these kinds of systems, and it can only be pushed a finite distance toward obscurity with each new language feature. I hope not, but...]That remains to be seen, but I think the buck stops at functions. The problem of duplicating templates for inout (lvalues) and rvalues stays, but I have an idea about that, that I might tell about tomorrow. Andrei
Dec 16 2006
== Quote from Andrei Alexandrescu (See Website For Email) ...That remains to be seen, but I think the buck stops at functions. The problem of duplicating templates for inout (lvalues) and rvalues stays, but I have an idea about that, that I might tell about tomorrow. AndreiDid you ever work out how to do this lvalue/rvalue idea? Kevin
Dec 19 2006
Kevin Bealer wrote:== Quote from Andrei Alexandrescu (See Website For Email) ...I had to leave to Romania before having the time to post thoughts on the lvalue/rvalue discussion I've had with Walter on Saturday. We both agree that it's a serious problem with D's type system that needs fixing (and that he needs to do all the work :o)). I've continued to think of the issue, at least enough to figure that the solution we initially thought of is not sound. Walter is reluctant to offering the ability to overload on lvalue/rvalue, while it turns out that that can't be avoided. Let's return to my litmus test - the identity function ident(e), which can snuggle any expression e and leave its semantics unchanged. My thesis is that this function is an important test of a language's power. The starting point would be: template ident(T) { T ident(T e) { return e; } inout T ident(inout T e) { return e; } } The problem with this approach is that it doesn't scale. Right now "inout" is about the only interesting storage class of a function parameter, but "lazy" comes to mind (which I hope to get rid of soon via a much better solution) and later on we'll have "const", and each of these combinations will mean one more duplication of the ident body (and, by extension, of any function that wants to just pass the storage class outside). Walter had an idea along the line: template ident(T) { return T ident(return T e) { return e; } } which allows you to reuse the return keyword as a symbolic placeholder for passing out the storage class. This solution is severely shortsighted in that it fixes ident and only ident, whereas the purpose of ident is to serve as a simplified case for functions with multiple parameters. So this fell as well. We then discussed another solution that I won't bore you with, as it's so wrong it hurts. My current thoughts navigate around two possible solutions. One is to make the storage part of the template parameters: template ident(S T) { S T ident(S T e) { return e; } } When two adjacent symbols appear in a template parameter list, they unambiguously denote a storage class followed by a type. So "S" can bind to things like "in", "inout" etc., while "T" can bind to types. In the example above, the compiler will deduce both S and T from the argument type. It already does that, so that's no extra difficulty. The key point that makes this scale is that you can bind S and T multiple times in a variadic template. Another interesting detail is that it clarifies that you can't solve the problem without somehow compiling two versions of the ident function. So in the end overloading on "inout" is a must. Another solution that works is to commit to the idea of associating the storage class with the parameter (and divorce it from the type entirely). In that case, the following syntax describes what happens: template ident(T) { storageof(e) T ident(storageof(e) T e) { return e; } } The storageof(symbol) meta-operator yields the storage of that symbol. The problem with this notation is that it uses a symbol without having seen it. That's not too bad (it already happens due to the way symbols at global scope are looked up) but in this case it does have a fishy smell. Another thing that I don't like it that the code obscures what's going on - namely that one ident will be generated for each storage class, even though that's not reflected in the parameter type list. Finally, one related but slightly different topic is the necessity of deduced return types for functions, e.g. by using "auto" to denote the return type. Automatic deduction of return types is very useful in that it allows compact template function definition - no more need for a template that defines a homonym function. With deduced argument types, ident can be written as: auto ident(S T)(S T e) { return e; } which is, I think, the Platonic ideal of ident as far as expressing it in D goes. AndreiThat remains to be seen, but I think the buck stops at functions. The problem of duplicating templates for inout (lvalues) and rvalues stays, but I have an idea about that, that I might tell about tomorrow. AndreiDid you ever work out how to do this lvalue/rvalue idea?
Dec 20 2006
Andrei Alexandrescu (See Website for Email) wrote:Kevin Bealer wrote:<snip> I'd like "auto" to appear on more places as it will make templates more accessible than ever before. For example, this notation has been suggested a couple of times: void func(auto x) { } I feel confident that having you and Walter on the issue, we should have a nice solution in no-time :) Sarbatori fericite! L. I'm actually leaving Romania to go to the Netherlands :)== Quote from Andrei Alexandrescu (See Website For Email) ...That remains to be seen, but I think the buck stops at functions. The problem of duplicating templates for inout (lvalues) and rvalues stays, but I have an idea about that, that I might tell about tomorrow. AndreiDid you ever work out how to do this lvalue/rvalue idea?
Dec 20 2006
Lionello Lunesu wrote:Andrei Alexandrescu (See Website for Email) wrote:Thanks for the kind wishes. One problem I see with the shortcut above is that the notation does not clarify whether "auto" catches both the type and the storage class. Even if it does, more linguistic machinery is needed to extract the type and storage class of x and to properly transport them, if needed, to the return type. So in the end the "auto" suggestion is a mere shortcut for: void func(S T)(S T x) { } with the difference that the storage and type entities are clearly bound to symbols, style that's more in the spirit of D. AndreiKevin Bealer wrote:<snip> I'd like "auto" to appear on more places as it will make templates more accessible than ever before. For example, this notation has been suggested a couple of times: void func(auto x) { }== Quote from Andrei Alexandrescu (See Website For Email) ...That remains to be seen, but I think the buck stops at functions. The problem of duplicating templates for inout (lvalues) and rvalues stays, but I have an idea about that, that I might tell about tomorrow. AndreiDid you ever work out how to do this lvalue/rvalue idea?
Dec 20 2006
Andrei Alexandrescu (See Website for Email) wrote:Lionello Lunesu wrote:Andrei Alexandrescu (See Website for Email) wrote:Kevin Bealer wrote:<snip> I'd like "auto" to appear on more places as it will make templates more accessible than ever before. For example, this notation has been suggested a couple of times: void func(auto x) { }== Quote from Andrei Alexandrescu (See Website For Email) ...That remains to be seen, but I think the buck stops at functions. The problem of duplicating templates for inout (lvalues) and rvalues stays, but I have an idea about that, that I might tell about tomorrow. AndreiDid you ever work out how to do this lvalue/rvalue idea?So in the end the "auto" suggestion is a mere shortcut for: void func(S T)(S T x) { } with the difference that the storage and type entities are clearly bound to symbols, style that's more in the spirit of D.The motivation I see for such a shortcut is that this template pattern is used so often that it is worth simplifying. Furthermore, the variables S and T are often just dummy variables to satisfy the requirements of the template; you don't even need these symbols, and they fill the namespace. Cheers, Reiner
Dec 20 2006
Reiner Pope wrote:Andrei Alexandrescu (See Website for Email) wrote:I'm definitely one for shortcuts, but let's not forget that at the moment we lack the feature that the shortcut is supposed to simplify. At this moment, "auto" helps solving the storage class parameterization issue as much as a great color for the seat covers helps designing an economic automobile. Let me illustrate further why ident is important and what solution we should have for it. Consider C's response to ident: #define IDENT(e) (e) Now, you can use IDENT with many C expressions (except those that have top-level commas), so we should be glad. Or should we? A true solution is to intercept the type of the expression and intelligently pass it as the return type of the function. In the general case, that means the language offers total control to the programmer of the types tossed around by a program. Instead, the C solution cheats its way around in that it has absolutely no grip on the expression type; it just gives the illusion that a function call is going on. Similarly, let's say that a group of revolutionaries convinces Walter (as I understand happened in case of using "length" and "$" inside slice expressions, which is a shame and an absolute disaster that must be undone at all costs) to implement "auto", therefore leading to the following implementation of ident: auto ident(auto x) { return x; } In the euphoria of the celebration, it will be initially forgotten that we have the shortcut without the feature, for the feature means having access to the exact type and storage of x, not finding a way to comfortably specify a deduction process. AndreiLionello Lunesu wrote:I'd like "auto" to appear on more places as it will make templates more accessible than ever before. For example, this notation has been suggested a couple of times: void func(auto x) { }So in the end the "auto" suggestion is a mere shortcut for: void func(S T)(S T x) { } with the difference that the storage and type entities are clearly bound to symbols, style that's more in the spirit of D.The motivation I see for such a shortcut is that this template pattern is used so often that it is worth simplifying. Furthermore, the variables S and T are often just dummy variables to satisfy the requirements of the template; you don't even need these symbols, and they fill the namespace.
Dec 20 2006
Andrei Alexandrescu (See Website For Email) wrote:Similarly, let's say that a group of revolutionaries convinces Walter (as I understand happened in case of using "length" and "$" inside slice expressions, which is a shame and an absolute disaster that must be undone at all costs) to implement "auto"This off-hand remark worries me. I presume that you mean being able to reference the length of a string, from inside the slice? (rather than simply the notation). And the problem being that it requires a sliceable entity to know its length? Or is the problem more serious than that? It's worrying because any change would break an enormous amount of code. These issues you're raising seem to be far too fundamental to be fixed in the next few days, casting grave doubts on whether a D1.0 release on Jan 1 is a good idea.
Dec 20 2006
Don Clugston wrote:Andrei Alexandrescu (See Website For Email) wrote:It would indeed break an enormous amount of code, but "all costs" includes "enormous costs". :o) A reasonable migration path is to deprecate them soon and make them illegal over the course of one year. A small book could be written on just how bad language design is using "length" and "$" to capture slice size inside a slice expression. I managed to write two lengthy emails to Walter about them, and just barely got started. Long story short, "length" introduces a keyword through the back door, effectively making any use of "length" anywhere unrecommended and highly fragile. Using "$" is a waste of symbolic real estate to serve a narrow purpose; the semantics isn't naturally generalized to its logical conclusion; and the choice of symbol itself been vastly better as it has count connotation in natural language, and making it into an operator would have fixed the generalization issue). As things stand now, the rules governing the popping up of "length" and "$" constitute a sudden boo-boo on an otherwise carefully designed expression landscape.Similarly, let's say that a group of revolutionaries convinces Walter (as I understand happened in case of using "length" and "$" inside slice expressions, which is a shame and an absolute disaster that must be undone at all costs) to implement "auto"This off-hand remark worries me. I presume that you mean being able to reference the length of a string, from inside the slice? (rather than simply the notation). And the problem being that it requires a sliceable entity to know its length? Or is the problem more serious than that? It's worrying because any change would break an enormous amount of code.These issues you're raising seem to be far too fundamental to be fixed in the next few days, casting grave doubts on whether a D1.0 release on Jan 1 is a good idea.The lvalue/rvalue issue is fundamental. I'm not in the position to assess whether it's a maker or breaker of D 1.0. The "length"/"$" issue is not fundamental the same way that C's declaration syntax, Java's throw specifications, C++'s use of "<" and ">" for templates, and Mao Zedong's refusal to use a toothbrush are not fundamental. It will "just" go down in history as a huge embarrassment and a good resource for cheap shooters and naysayers. If I understand its genesis, it will also be a canonical example of why design by committee is bad. Andrei
Dec 20 2006
Andrei Alexandrescu (See Website For Email) wrote:Don Clugston wrote:Are you suggesting either / both: slice = array[x .. array.length]; ? Since length and $ are pretty easily grep-able in the context of slice syntax, perhaps it's not a "huge" issue if these were deprecated now and then not supported in the span of a couple of 1.0.x releases or so (instead of a year)?Andrei Alexandrescu (See Website For Email) wrote:It would indeed break an enormous amount of code, but "all costs" includes "enormous costs". :o) A reasonable migration path is to deprecate them soon and make them illegal over the course of one year. A small book could be written on just how bad language design is using "length" and "$" to capture slice size inside a slice expression. I managed to write two lengthy emails to Walter about them, and just barely got started. Long story short, "length" introduces a keyword through the back door, effectively making any use of "length" anywhere unrecommended and highly fragile. Using "$" is a waste of symbolic real estate to serve a narrow purpose; the semantics isn't naturally generalized to its logical conclusion; and the choice of symbol itself been vastly better as it has count connotation in natural language, and making it into an operator would have fixed the generalization issue). As things stand now, the rules governing the popping up of "length" and "$" constitute a sudden boo-boo on an otherwise carefully designed expression landscape.Similarly, let's say that a group of revolutionaries convinces Walter (as I understand happened in case of using "length" and "$" inside slice expressions, which is a shame and an absolute disaster that must be undone at all costs) to implement "auto"This off-hand remark worries me. I presume that you mean being able to reference the length of a string, from inside the slice? (rather than simply the notation). And the problem being that it requires a sliceable entity to know its length? Or is the problem more serious than that? It's worrying because any change would break an enormous amount of code.Since one of the main drivers for 1.0 by Jan. 1, 2007 is to encourage / solidify library development, and since library design could be affected in a large way by this issue, I'd say it's best to figure this out before releasing 1.0.These issues you're raising seem to be far too fundamental to be fixed in the next few days, casting grave doubts on whether a D1.0 release on Jan 1 is a good idea.The lvalue/rvalue issue is fundamental. I'm not in the position to assess whether it's a maker or breaker of D 1.0.The "length"/"$" issue is not fundamental the same way that C's declaration syntax, Java's throw specifications, C++'s use of "<" and ">" for templates, and Mao Zedong's refusal to use a toothbrush are not fundamental. It will "just" go down in history as a huge embarrassment and a good resource for cheap shooters and naysayers. If I understand its genesis, it will also be a canonical example of why design by committee is bad.If indeed it will be an embarrassment, better to take care of this sooner (pre-1.0) rather than later, IMHO. Thanks, - Dave
Dec 20 2006
On Wed, 20 Dec 2006 06:24:28 -0800, Andrei Alexandrescu (See Website For Email) wrote:A small book could be written on just how bad language design is using "length" and "$" to capture slice size inside a slice expression. I managed to write two lengthy emails to Walter about them, and just barely got started.Please share your thoughts here if you can too.Long story short, "length" introduces a keyword through the back door, effectively making any use of "length" anywhere unrecommended and highly fragile.There is no arguement from me on that score.Using "$" is a waste of symbolic real estate to serve a narrow purpose;By that do you mean that the symbol "$" could be better utilized elsewhere in the language?the semantics isn't naturally generalized to its logical conclusion;I have no idea what that statement means. The semantics of *what*? Define "naturally generalized" in absolute terms without recourse to opinion. What is the "logical conclusion" you talk of?and the choice of symbol itself been vastly better as it has count connotation in natural languageThe concept was to have a very short symbol to represent the array's being one of them. Walter rejected that one because it was already used for something else - the 'special token sequences' construct, such as "#line". Also symbols that consisted of identifier characters all have the same problem as "length" does. I favoured "$" because it was a single-character symbol and is already used in similar concepts inside regular expression syntaxes. Although the exact symbol that is 'finally' decided upon is not a burning issue for me, there would have to be a very solid argument for the provable superiority of that one symbol over the rest. And currently, the choiceand making it into an operator would have fixed the generalization issueWould this lead to an opLength() method available for overloading?As things stand now, the rules governing the popping up of "length" and "$" constitute a sudden boo-boo on an otherwise carefully designed expression landscape.Still sounds like an opinion and not a fact, in my opinion ;-) -- Derek
Dec 20 2006
Derek Parnell wrote:The concept was to have a very short symbol to represent the array's being one of them. Walter rejected that one because it was already used for something else - the 'special token sequences' construct, such as "#line". Also symbols that consisted of identifier characters all have the same problem as "length" does. I favoured "$" because it was a single-character symbol and is already used in similar concepts inside regular expression syntaxes.The "[..$]" syntax is also present in ColdC and its relatives (including my Bovis), so it was familiar to me from the beginning. (That said I still harbor thoughts that $ could be used for other things... but honestly, I think the syntax would be unambiguous: a lone $ as the right hand side of a slice expression should easily enough be distinguishable from a $ anywhere followed by something, like an identifier.) Which leads me to another thought. One other operator that ColdC and family posesses is the for list splicing. Useless sample ColdC: The 'result' variable now equals {1, 2, 3, 4, 5, 6}. Perhaps we could get something similar in D, also using , and then I think would possibly be a logical choice for denoting "until end" in slices. I also think an 'opLength' operator is not a bad idea, but it might be best just to require a .length property of some kind be present, since presumably any class that exposes itself to slicing would likely also have a length concept of some kind. (Contrary examples welcome.) -- Chris Nicholson-Sauls
Dec 20 2006
Chris Nicholson-Sauls wrote:The "[..$]" syntax is also present in ColdC and its relatives (including my Bovis), so it was familiar to me from the beginning. (That said I still harbor thoughts that $ could be used for other things... but honestly, I think the syntax would be unambiguous: a lone $ as the right hand side of a slice expression should easily enough be distinguishable from a $ anywhere followed by something, like an identifier.)FWIW $ is not only used for the RHS of a slice char[] str; str[$/2..$]; // 2nd half of array str[$-1]; // last element in array str[$-5..$]; // last 5 things in array str[$-10..10]; // um... well... you get the ideaWhich leads me to another thought. One other operator that ColdC and family posesses is the for list splicing. Useless sample ColdC: The 'result' variable now equals {1, 2, 3, 4, 5, 6}.I would think this would be the same thing. auto foo = [1,2,3]; auto bar = [4,5,6]; auto result = foo ~ bar; am I missing somethign?-- Chris Nicholson-Sauls
Dec 20 2006
BCS wrote:Chris Nicholson-Sauls wrote:Ack. Having never used anything quite like that before, I guess I had assumed the $ only had meaning as I described above. Still, it could be possible.The "[..$]" syntax is also present in ColdC and its relatives (including my Bovis), so it was familiar to me from the beginning. (That said I still harbor thoughts that $ could be used for other things... but honestly, I think the syntax would be unambiguous: a lone $ as the right hand side of a slice expression should easily enough be distinguishable from a $ anywhere followed by something, like an identifier.)FWIW $ is not only used for the RHS of a slice char[] str; str[$/2..$]; // 2nd half of array str[$-1]; // last element in array str[$-5..$]; // last 5 things in array str[$-10..10]; // um... well... you get the ideaNot really, no. But consider: It becomes part of the literal syntax, which makes things cleaner in most elaborate cases. Just something I enjoy over there that I wouldn't mind seeing from time to time over here. :) -- Chris Nicholson-SaulsWhich leads me to another thought. One other operator that ColdC and family posesses is the for list splicing. Useless sample ColdC: The 'result' variable now equals {1, 2, 3, 4, 5, 6}.I would think this would be the same thing. auto foo = [1,2,3]; auto bar = [4,5,6]; auto result = foo ~ bar; am I missing somethign?
Dec 20 2006
Chris Nicholson-Sauls wrote:BCS wrote:These are very possible and are at times very useful. Though the last example is perhaps pushing it in the latter department ;). But yeah, $ is valid inside any [] pair, whether as (part of) an index or as (part of) either side of a slice.Chris Nicholson-Sauls wrote:Ack. Having never used anything quite like that before, I guess I had assumed the $ only had meaning as I described above. Still, it could be possible.The "[..$]" syntax is also present in ColdC and its relatives (including my Bovis), so it was familiar to me from the beginning. (That said I still harbor thoughts that $ could be used for other things... but honestly, I think the syntax would be unambiguous: a lone $ as the right hand side of a slice expression should easily enough be distinguishable from a $ anywhere followed by something, like an identifier.)FWIW $ is not only used for the RHS of a slice char[] str; str[$/2..$]; // 2nd half of array str[$-1]; // last element in array str[$-5..$]; // last 5 things in array str[$-10..10]; // um... well... you get the ideaYou don't need to surround a non-array by [] if concatenating with an array of the same type: result = foo ~ 0 ~ bar; will work just fine.Not really, no. But consider:Which leads me to another thought. One other operator that ColdC and family posesses is the for list splicing. Useless sample ColdC: The 'result' variable now equals {1, 2, 3, 4, 5, 6}.I would think this would be the same thing. auto foo = [1,2,3]; auto bar = [4,5,6]; auto result = foo ~ bar; am I missing somethign?result = 42 ~ someFunc();result = foo ~ 1 ~ foo ~ 2;result = [3, 6] ~ myConst ~ 9; The first []s are still needed, unless you add parentheses to group the 6 to myConst (and possibly the 9), but the last ones are unnecessary. Also, .dup is completely useless here (presuming myConst is an array) since ~ always allocates. (Note that repeated ~s in the same expression only allocate once though, at least in DMD)
Dec 20 2006
Chris Nicholson-Sauls wrote:BCS wrote:I think that syntax is a little more attractive than ~ for some cases. It makes it clearer that you're building a list. We don't say [] ~ 1 ~ 2 ~ 3 to make the array [1,2,3], after all. But for that reason (because it's generalizing array literals) I think it should use [] instead of {}. So result = [ foo, 0, bar]; To me that certainly does make it clearer that I'm making a list out of lists. The as a special symbol just for this kind of bothers me, though. And how would it work for user-defined types? I guess it could just be turned into the equivalent opCat calls... --bbChris Nicholson-Sauls wrote:Not really, no. But consider: It becomes part of the literal syntax, which makes things cleaner in most elaborate cases. Just something I enjoy over there that I wouldn't mind seeing from time to time over here. :)
Dec 20 2006
Derek Parnell wrote:On Wed, 20 Dec 2006 06:24:28 -0800, Andrei Alexandrescu (See Website For Email) wrote:Gladly; I dug my email and let me share a couple of excerpts. --------- int length = 5; int[] a = new int[length * 2]; int[] b = a[length .. length * 2]; int c = a[length - 1 .. (b[0 .. length])[0]); In each of its uses, length has a different semantics. The behavior is well-defined for all cases, but nonintuitive and about as pleasant as nails on the blackboard. Now D has a compile-time option to ban the "length" name in scopes in which the slice operator is used. That would render the example above illegal. There is also a rule that identifiers in nested scopes cannot mask one another. So length will be banned from *any* scope that nests a scope using a slice: int length; if (a) { foreach (b; c) { while (d) { switch (e) { case f: g = h[0 .. length - 1]; ... } } } } This code will not compile. Worse, it *will* compile until you add the slice operation. Combining the two rules and taking them to their logical conclusion, any code using "length" is frail because there's always a risk that somebody might insert a slice, rendering the entire function uncompilable. What happened is that now "length" has become a backdoor-introduced keyword. Books will advise users to never use it even when it works, coding standards will ban it, language lawyers will use it to detract D, and users of other languages will smile condescendingly and stay with their languages. There are a few ways out of it. "length" could be actually made a keyword. But even that one isn't very uniform, and steals yet another good identifier name. Another way out of it is to ban "length" but stick with "$". But "$" has another bunch of problems. It's a special character used only once, and only in a very particular situation. There is no general concept standing behind its usage: it sticks out like a sore thumb. "$" isn't the last index in an array. It's that only when used inside a slice, and refers only to the innermost index of the array. Quite a waste of a special character out there, and to little usefulness. But if we made "$" into an operator identifying the last element of _any_ array, which could refer to the last element of _the left-hand side_ array if we so want, then all of a sudden it becomes useful in a myriad of situations: int i = a[$ - 1]; // get last element int i = a[$b - 1]; // get a's element at position b.length - 1 if (a[$ - 1] == x) { ... } if ($a > 0) { ... } if ($a == $b) { ... } swap(a[0], a[$ - 1]); // swap first and last element --------------- Grammar for nullary/unary $: --------------- I think I nailed down the way the count operator $ can work in a manner that's terse, expressive, and safe. My basic goal is to enable the operator $ to be unary (applying to an array) to return its size, and also nullary (applying to nothing) to implicitly mean "fetch the size of the innermost array in the expression". So this code should work: int[] foo; foo[$ - 1]; // refers to foo's last element foo[$foo - 1]; // same int[][] bar; bar[foo[$]]; // refers to bar indexed with foo's last element bar[foo[$bar]]; // refers to bar indexed with foo's element at $bar To insert my operator $ within D's grammar, go to the grammar page: http://www.digitalmars.com/d/expression.html$UnaryExpression and scroll down to Unary Expression. There, add the following rules: UnaryExpression: PostfixExpression & UnaryExpression ... etc. etc. ... $ Identifier $ PostfixExpression . Identifier $ PostfixExpression ( ) $ PostfixExpression ( ArgumentList ) $ IndexExpression $ SliceExpression $ ArrayLiteral $ ( Expression ) Now a unary expression can be the $ operator followed by an identifier, a member access, a function call, an array access, or a slice expression (awesome! pick the size of the slice!), a literal array (for conformity), or a parenthesized expression. Perfect! But we haven't yet filled the role of $ as a nullary operator. To do so, let's go in the grammar to http://www.digitalmars.com/d/expression.html$PrimaryExpression and append one more rule to it the PrimaryExpression rule: PrimaryExpression: Identifier .Identifier ... etc. etc. ... $ Now the grammar is unambiguous and will properly distinguish unary and nullary uses of the $ operator. This is more elegant than the current crap with "$" and "length" popping up. Besides, you can now use $ in many more places than inside []s. However, the grammar size does increase quite a bit, which is more fuss than I hoped for just one operator. A simpler grammar would have been to simply allow: UnaryExpression: PostfixExpression & UnaryExpression ... etc. etc. ... $ PostfixExpression But this would have been ambiguous. If the compiler sees "$-1", then the bad grammar says that's a unary use of $ because -1 is a PostfixExpression. But that's not what we wanted! We wanted $ to be nullary. That's why I needed to put all the cases in UnaryExpression. AndreiA small book could be written on just how bad language design is using "length" and "$" to capture slice size inside a slice expression. I managed to write two lengthy emails to Walter about them, and just barely got started.Please share your thoughts here if you can too.
Dec 21 2006
Andrei Alexandrescu (See Website For Email) wrote:Derek Parnell wrote:<snipped excerpts> Wow, I understand it now. I only hope that at least 'length' will be deprecated before 1.0. I like your dollars. I'm not so good with grammars, will your proposal also work for user defined types?On Wed, 20 Dec 2006 06:24:28 -0800, Andrei Alexandrescu (See Website For Email) wrote:Gladly; I dug my email and let me share a couple of excerpts.A small book could be written on just how bad language design is using "length" and "$" to capture slice size inside a slice expression. I managed to write two lengthy emails to Walter about them, and just barely got started.Please share your thoughts here if you can too.
Dec 21 2006
Lutger wrote:Wow, I understand it now. I only hope that at least 'length' will be deprecated before 1.0. I like your dollars.Well, just don't take'em away from my bank account :o).I'm not so good with grammars, will your proposal also work for user defined types?The plan is that $expression is rewritten into (expression).length. The consistent thing to do is to make that into an onXyz() function, but I don't find this name inconsistency jarring. Andrei
Dec 21 2006
Andrei Alexandrescu (See Website For Email) wrote:A simpler grammar would have been to simply allow: UnaryExpression: PostfixExpression & UnaryExpression ... etc. etc. ... $ PostfixExpression But this would have been ambiguous. If the compiler sees "$-1", then the bad grammar says that's a unary use of $ because -1 is a PostfixExpression. But that's not what we wanted! We wanted $ to be nullary. That's why I needed to put all the cases in UnaryExpression.Nice post, and one heck of an argument! FWIW, I advocated something similar during the last round of debates before the '$' operator was introduced. What I wanted to see was '$' to become like 'this' within slice and array expressions, so that the issues regarding 'length' could be resolved. In essence one could simply say '$.length' and mean 'the length of the current array': b[0 .. $.length]; a[0 .. $.getIndexOf(';')]; So in essence, every use of '$' would be a 'nullary' operator - an alias if you will. I'd imagine that extending things in this manner would simplify things grammatically while allowing for a wider category of uses. However, it doesn't solve the issue that you brought up, and that I've quoted above. c[$-1]; It looks like it should be an implicit cast of the '$' to a size_t (length), via it's use in an expression. Any thoughts on this? -- - EricAnderton at yahoo
Dec 21 2006
Pragma wrote:Andrei Alexandrescu (See Website For Email) wrote:I rather like this. And I think I liked it then, too... if not, oh well.A simpler grammar would have been to simply allow: UnaryExpression: PostfixExpression & UnaryExpression ... etc. etc. ... $ PostfixExpression But this would have been ambiguous. If the compiler sees "$-1", then the bad grammar says that's a unary use of $ because -1 is a PostfixExpression. But that's not what we wanted! We wanted $ to be nullary. That's why I needed to put all the cases in UnaryExpression.Nice post, and one heck of an argument! FWIW, I advocated something similar during the last round of debates before the '$' operator was introduced. What I wanted to see was '$' to become like 'this' within slice and array expressions, so that the issues regarding 'length' could be resolved. In essence one could simply say '$.length' and mean 'the length of the current array': b[0 .. $.length]; a[0 .. $.getIndexOf(';')]; So in essence, every use of '$' would be a 'nullary' operator - an alias if you will.I'd imagine that extending things in this manner would simplify things grammatically while allowing for a wider category of uses. However, it doesn't solve the issue that you brought up, and that I've quoted above. c[$-1]; It looks like it should be an implicit cast of the '$' to a size_t (length), via it's use in an expression. Any thoughts on this?If $ is like a 'this', then it ought to be have semantically the same, so if $ is a class/struct with an opCast to size_t defined, the obvious happens. If its anything else, it ought to be a compile time error, perhaps suggesting you had meant '$.length' instead. -- Chris Nicholson-Sauls
Dec 21 2006
Chris Nicholson-Sauls wrote:Pragma wrote:In both of those cases the use seems rather silly to me because a and b are both single characters to begin with. Might as well just type b[0 .. b.length]; a[0 .. a.getIndexOf(';')]; instead. But I get the point. Sometimes you have g_openSocketHandles[0 .. g_openSocketHandles.getIndexOf()] But maybe just allowing 'this' in the brackets is enough there, without going on and abbreviating it to $. The $==.length proposal at least has the advantage of being backwards compatible.Andrei Alexandrescu (See Website For Email) wrote:A simpler grammar would have been to simply allow: UnaryExpression: PostfixExpression & UnaryExpression ... etc. etc. ... $ PostfixExpression But this would have been ambiguous. If the compiler sees "$-1", then the bad grammar says that's a unary use of $ because -1 is a PostfixExpression. But that's not what we wanted! We wanted $ to be nullary. That's why I needed to put all the cases in UnaryExpression.Nice post, and one heck of an argument! FWIW, I advocated something similar during the last round of debates before the '$' operator was introduced. What I wanted to see was '$' to become like 'this' within slice and array expressions, so that the issues regarding 'length' could be resolved. In essence one could simply say '$.length' and mean 'the length of the current array': b[0 .. $.length]; a[0 .. $.getIndexOf(';')]; So in essence, every use of '$' would be a 'nullary' operator - an alias if you will.Not sure I like $==this as must as $==.length. I have pressing need for a brief syntax for specifying the length, but no such thing for a shorter form of 'this'. But anyway, if you're going to allow '$' to mean 'this' inside brackets, first you first need the language feature that allows 'this' to be used inside brackets in the first place. And maybe if you have that you'll find it's sufficient. Another thing is if you're going to allow 'this' in brackets, then you should take the idea to its logical conclusions and allow it in member function call parameter lists too. That might be nice for things like enum paramters. Of course if $ gets translated into a call to a method/property, you could have it your way if you prefer for your classes. Just use opDollar() { return this; } and voila! You can use your $.getIndexOf(';'). --bbI rather like this. And I think I liked it then, too... if not, oh well.I'd imagine that extending things in this manner would simplify things grammatically while allowing for a wider category of uses. However, it doesn't solve the issue that you brought up, and that I've quoted above. c[$-1]; It looks like it should be an implicit cast of the '$' to a size_t (length), via it's use in an expression. Any thoughts on this?If $ is like a 'this', then it ought to be have semantically the same, so if $ is a class/struct with an opCast to size_t defined, the obvious happens. If its anything else, it ought to be a compile time error, perhaps suggesting you had meant '$.length' instead. -- Chris Nicholson-Sauls
Dec 21 2006
Bill Baxter wrote:Chris Nicholson-Sauls wrote:The problem with actually using the 'this' keyword in place of $ is one of ambiguity. Given a collection class 'Set' and some other class 'Foo', what to do if a 'this' is used within a slice of a 'Set' instance within a member of 'Foo'? Does it evaluate to the Foo referance it would in all other cases? Or to a Set referanc? And if the latter, how to get the Foo referance if that really is what I wanted? The $ would have to be different from 'this' in the classes' sense. Perhaps it would be better to call it a 'self' or even a 'with' than a 'this'. -- Chris Nicholson-SaulsPragma wrote:In both of those cases the use seems rather silly to me because a and b are both single characters to begin with. Might as well just type b[0 .. b.length]; a[0 .. a.getIndexOf(';')]; instead. But I get the point. Sometimes you have g_openSocketHandles[0 .. g_openSocketHandles.getIndexOf()] But maybe just allowing 'this' in the brackets is enough there, without going on and abbreviating it to $. The $==.length proposal at least has the advantage of being backwards compatible.Andrei Alexandrescu (See Website For Email) wrote:A simpler grammar would have been to simply allow: UnaryExpression: PostfixExpression & UnaryExpression ... etc. etc. ... $ PostfixExpression But this would have been ambiguous. If the compiler sees "$-1", then the bad grammar says that's a unary use of $ because -1 is a PostfixExpression. But that's not what we wanted! We wanted $ to be nullary. That's why I needed to put all the cases in UnaryExpression.Nice post, and one heck of an argument! FWIW, I advocated something similar during the last round of debates before the '$' operator was introduced. What I wanted to see was '$' to become like 'this' within slice and array expressions, so that the issues regarding 'length' could be resolved. In essence one could simply say '$.length' and mean 'the length of the current array': b[0 .. $.length]; a[0 .. $.getIndexOf(';')]; So in essence, every use of '$' would be a 'nullary' operator - an alias if you will.Not sure I like $==this as must as $==.length. I have pressing need for a brief syntax for specifying the length, but no such thing for a shorter form of 'this'. But anyway, if you're going to allow '$' to mean 'this' inside brackets, first you first need the language feature that allows 'this' to be used inside brackets in the first place. And maybe if you have that you'll find it's sufficient.I rather like this. And I think I liked it then, too... if not, oh well.I'd imagine that extending things in this manner would simplify things grammatically while allowing for a wider category of uses. However, it doesn't solve the issue that you brought up, and that I've quoted above. c[$-1]; It looks like it should be an implicit cast of the '$' to a size_t (length), via it's use in an expression. Any thoughts on this?If $ is like a 'this', then it ought to be have semantically the same, so if $ is a class/struct with an opCast to size_t defined, the obvious happens. If its anything else, it ought to be a compile time error, perhaps suggesting you had meant '$.length' instead. -- Chris Nicholson-Sauls
Dec 21 2006
Pragma wrote:b[0 .. $.length]; a[0 .. $.getIndexOf(';')]; So in essence, every use of '$' would be a 'nullary' operator - an alias if you will.This isn't going to be agreeable to most since the purpose of $ in the first place was to save typing.I'd imagine that extending things in this manner would simplify things grammatically while allowing for a wider category of uses. However, it doesn't solve the issue that you brought up, and that I've quoted above. c[$-1]; It looks like it should be an implicit cast of the '$' to a size_t (length), via it's use in an expression. Any thoughts on this?I'd rather have $ defined everywhere to mean length, which is useful outside [] as well. Andrei P.S. Maybe there's a misunderstanding? The grammar I sent does not have a problem w.r.t. unary vs. nullary; it's just a tad more complicated to avoid ambiguity.
Dec 22 2006
Andrei Alexandrescu (See Website For Email) wrote:Pragma wrote:Understood. I just figured I'd throw that out there in case it had any merit in the current discussion.b[0 .. $.length]; a[0 .. $.getIndexOf(';')]; So in essence, every use of '$' would be a 'nullary' operator - an alias if you will.This isn't going to be agreeable to most since the purpose of $ in the first place was to save typing.I'd imagine that extending things in this manner would simplify things grammatically while allowing for a wider category of uses. However, it doesn't solve the issue that you brought up, and that I've quoted above. c[$-1]; It looks like it should be an implicit cast of the '$' to a size_t (length), via it's use in an expression. Any thoughts on this?I'd rather have $ defined everywhere to mean length, which is useful outside [] as well.Andrei P.S. Maybe there's a misunderstanding? The grammar I sent does not have a problem w.r.t. unary vs. nullary; it's just a tad more complicated to avoid ambiguity.Ah, I understand then. The way you explained the grammar changes, it looked to me as though there was still an issue. -- - EricAnderton at yahoo
Dec 22 2006
Andrei Alexandrescu (See Website For Email) wrote:Another way out of it is to ban "length" but stick with "$". But "$" has another bunch of problems. It's a special character used only once, and only in a very particular situation. There is no general concept standing behind its usage: it sticks out like a sore thumb. "$" isn't the last index in an array. It's that only when used inside a slice, and refers only to the innermost index of the array. Quite a waste of a special character out there, and to little usefulness. But if we made "$" into an operator identifying the last element of _any_ array, which could refer to the last element of _the left-hand side_ array if we so want, then all of a sudden it becomes useful in a myriad of situations:Provided that some such expansion path for "$" exists, it would seem to be adequate for D 1.0 to just remove "length". And this could be done by Jan 1.
Dec 21 2006
Don Clugston wrote:Andrei Alexandrescu (See Website For Email) wrote:That is correct. One advantage of the unary/nullary $ is that it's a strict extension to today's $. Thus, no existing code will be invalidated by the new semantics of $. AndreiAnother way out of it is to ban "length" but stick with "$". But "$" has another bunch of problems. It's a special character used only once, and only in a very particular situation. There is no general concept standing behind its usage: it sticks out like a sore thumb. "$" isn't the last index in an array. It's that only when used inside a slice, and refers only to the innermost index of the array. Quite a waste of a special character out there, and to little usefulness. But if we made "$" into an operator identifying the last element of _any_ array, which could refer to the last element of _the left-hand side_ array if we so want, then all of a sudden it becomes useful in a myriad of situations:Provided that some such expansion path for "$" exists, it would seem to be adequate for D 1.0 to just remove "length". And this could be done by Jan 1.
Dec 22 2006
Andrei Alexandrescu (See Website For Email) wrote:But if we made "$" into an operator identifying the last element of _any_ array, which could refer to the last element of _the left-hand side_ array if we so want, then all of a sudden it becomes useful in a myriad of situations: int i = a[$ - 1]; // get last element int i = a[$b - 1]; // get a's element at position b.length - 1 if (a[$ - 1] == x) { ... } if ($a > 0) { ... } if ($a == $b) { ... } swap(a[0], a[$ - 1]); // swap first and last elementPlease give some thought to the case where a and b are of types not easily characterized by a single '.length'. Matrix classes, or more generally multidimensional array classes being the canonical examples. For those cases it is desirable to be able to have a '$' with different meaning "per axis". For those cases a we could have a small extension to your proposal. Have $b translate to b.length, yes, but also have $[3]b and $(1)b translate to to b.length[3] and b.length(1), respectively. Seeing that, it makes me think perhaps $ would be better as a post-fix unary operator. Then we'd have b$ --> b.length and b$[3] --> b.length[3]. Then of course the next step is to have a parameter number automatically passed to the length method given and expression like a[$-1,$-1] so that a[$-1,$-1] ==> a[$[0]-1,$[1]-1] ==> a[a$[0]-1,a$[1]-1] ==> a[a.length[0],a.length[1]] The compiler can decide whether to do indexing or not based on whether .length results in an indexable value. Finally, in general I think the choice of name 'length' is unfortunate because of it's implication of linearity. But it's not too late. If $ becomes associated with .size rather than .length in user types then everything will be ok. For built-in arrays .length can become a synonym for .size, just as it is with std::string in C++. C++/STL got this one right. For generic containers .size is a much better name. --bb
Dec 22 2006
Bill Baxter wrote:Andrei Alexandrescu (See Website For Email) wrote:Then of course the next step is to have a parameter number automatically passed to the length method given and expression like a[$-1,$-1] so that a[$-1,$-1] ==> a[$[0]-1,$[1]-1] ==> a[a$[0]-1,a$[1]-1] ==> a[a.length[0],a.length[1]]Slight typo there. Last line should of course have been: ==> a[a.length[0]-1,a.length[1]-1]The compiler can decide whether to do indexing or not based on whether .length results in an indexable value. Finally, in general I think the choice of name 'length' is unfortunate because of it's implication of linearity. But it's not too late. If $ becomes associated with .size rather than .length in user types then everything will be ok. For built-in arrays .length can become a synonym for .size, just as it is with std::string in C++. C++/STL got this one right. For generic containers .size is a much better name.Another thing which occurred to me is that if the meaning of $ becomes tied to "size" rather than "length", then then you also have the mnemonic of $ looking like an 's' as in 'size'. I also still think making it a postfix operator makes sense. --bb
Dec 21 2006
Bill Baxter wrote:Andrei Alexandrescu (See Website For Email) wrote:I did. The thing with language design is that it's easy to either underdo or overdo it, and that where underdoing or overdoing starts is highly subjective. IMHO the current meaning of "$" is a good example of underdoing. The "$expression" meaning "(expression).length" is (again IMHO) just right. I use collection.size() all the time in C++, and scalar( array) or $#array all the time in Perl, inside and outside index expressions. So I'd be happy to have that. Taking it to the next step of meaning any subdimension of a multidimensional (or fractal, heh) structure is, IMHO, overdoing because I can think of few use examples that are both frequent enough and interesting enough. AndreiBut if we made "$" into an operator identifying the last element of _any_ array, which could refer to the last element of _the left-hand side_ array if we so want, then all of a sudden it becomes useful in a myriad of situations: int i = a[$ - 1]; // get last element int i = a[$b - 1]; // get a's element at position b.length - 1 if (a[$ - 1] == x) { ... } if ($a > 0) { ... } if ($a == $b) { ... } swap(a[0], a[$ - 1]); // swap first and last elementPlease give some thought to the case where a and b are of types not easily characterized by a single '.length'. Matrix classes, or more generally multidimensional array classes being the canonical examples. For those cases it is desirable to be able to have a '$' with different meaning "per axis".
Dec 22 2006
Andrei Alexandrescu (See Website for Email) wrote:Bill Baxter wrote:Maybe so. Multidimensional arrays seem as common as air from where I sit, but I can see that not everyone works with such things every day. If $ really did become synonymous with .length (or preferably .size) then one could have .length return an array rather than a simple number. In that case multi-dim'ers could have M[5..$[0]-1, 0..$[1]-5]. Eh. Not so pretty. For comparison, in Python that would be M[5:,:-5], and in Matlab that would be M(6:end, 1:end-4). Both of those look much better to me. If the index to go with $ could be supplied automatically by the compiler then D could have M[5..$-1, 0..$-5]. Taking a different tack, I wonder if repeated indexing can be made as efficient (or nearly) as a single multi-index? M[5..$-1][0..$-5] That's much easier to look at than M[5..$[0]-1, 0..$[1]-5], at least. And it's more general in the sense that from looking at the expression, M looks just like a standard Type[][] array. Hmm. I'll play with that. I think it's at least technically possible, now that D has the ability to override opAssign. --bbAndrei Alexandrescu (See Website For Email) wrote:I did. The thing with language design is that it's easy to either underdo or overdo it, and that where underdoing or overdoing starts is highly subjective. IMHO the current meaning of "$" is a good example of underdoing. The "$expression" meaning "(expression).length" is (again IMHO) just right. I use collection.size() all the time in C++, and scalar( array) or $#array all the time in Perl, inside and outside index expressions. So I'd be happy to have that. Taking it to the next step of meaning any subdimension of a multidimensional (or fractal, heh) structure is, IMHO, overdoing because I can think of few use examples that are both frequent enough and interesting enough.But if we made "$" into an operator identifying the last element of _any_ array, which could refer to the last element of _the left-hand side_ array if we so want, then all of a sudden it becomes useful in a myriad of situations: int i = a[$ - 1]; // get last element int i = a[$b - 1]; // get a's element at position b.length - 1 if (a[$ - 1] == x) { ... } if ($a > 0) { ... } if ($a == $b) { ... } swap(a[0], a[$ - 1]); // swap first and last elementPlease give some thought to the case where a and b are of types not easily characterized by a single '.length'. Matrix classes, or more generally multidimensional array classes being the canonical examples. For those cases it is desirable to be able to have a '$' with different meaning "per axis".
Dec 22 2006
Andrei Alexandrescu (See Website For Email) wrote:A simpler grammar would have been to simply allow: UnaryExpression: PostfixExpression & UnaryExpression ... etc. etc. ... $ PostfixExpression But this would have been ambiguous. If the compiler sees "$-1", then the bad grammar says that's a unary use of $ because -1 is a PostfixExpression. But that's not what we wanted! We wanted $ to be nullary. That's why I needed to put all the cases in UnaryExpression.Wouldn't that ambiguity be fixed by making $ a postfix unary operator instead? Or would that just introduce different ambiguities? --bb
Dec 24 2006
Andrei Alexandrescu (See Website For Email) wrote:Don Clugston wrote:That hadn't occurred to me, but you're right. I never use length in that context precisely because it does look like it could be a local identifier, whereas I know it'll be clear it's not if I use $. Also "length" is just too long to be of much use to me as a shortcut. If I'm going to be that verbose I might as well type out the whole "varname.length".Andrei Alexandrescu (See Website For Email) wrote:It would indeed break an enormous amount of code, but "all costs" includes "enormous costs". :o) A reasonable migration path is to deprecate them soon and make them illegal over the course of one year. A small book could be written on just how bad language design is using "length" and "$" to capture slice size inside a slice expression. I managed to write two lengthy emails to Walter about them, and just barely got started. Long story short, "length" introduces a keyword through the back door, effectively making any use of "length" anywhere unrecommended and highly fragile.Similarly, let's say that a group of revolutionaries convinces Walter (as I understand happened in case of using "length" and "$" inside slice expressions, which is a shame and an absolute disaster that must be undone at all costs) to implement "auto"This off-hand remark worries me. I presume that you mean being able to reference the length of a string, from inside the slice? (rather than simply the notation). And the problem being that it requires a sliceable entity to know its length? Or is the problem more serious than that? It's worrying because any change would break an enormous amount of code.Using "$" is a waste of symbolic real estate to serve a narrow purpose; the semantics isn't naturally generalized to its logical conclusion;I do use this one, but I agree. It is unnecessarily special cased for built-in array types. For user-defined types, in 'myvar[0..$]' the $ does not expand to 'myvar.length' as one would naturally expect it to. Or any sort of opLength() call. It's just a syntax error.and the choice of symbol itself been vastly better as it has count connotation in natural language, and making it into an operator would have fixed the generalization issue).I think you'll have to admit that's just your personal taste there. Using $ to indicate 'end' is a regexp thing, but regexp's go way beyond Perl. I don't really care what it is as long as there's an terse way to specify 'the end' in an indexing expression.As things stand now, the rules governing the popping up of "length" and "$" constitute a sudden boo-boo on an otherwise carefully designed expression landscape.After trying to write a multi-dimensional array class, my opinion is that D slice support could use some upgrades overall. What I'd like to see: --MultiRange Slice-- * A way to have multiple ranges in a slice, and a mix slice of and non-slice indices: A[i..j, k..m] A[i..j, p, k..m] I'm not saying built-in arrays like int[] should allow the above expressions, but that at least user types should be allowed to have such opSlice methods. (Currently opSlice's are limited to having 2 arguments that represent the values that appear on either side of a single '..' token. You can only have two arguments max, but the arguments can be of any type.) The problem is that opSlice has to look like opSlice(T1 lo, T2 hi) right now -- just two parameters (or zero). One possible solution is to turn a single i..j into a single int[2] argument (or a mytype[2], for the general case). But that means one won't be able to distinguish A[[1,3]] from A[1..3]. It also means more interesting extensions to slice syntax, like adding a stepsize on a range, will be ruled out. Another solution is a built-in slice type. Ranges like a..b would get converted to slice instances automatically. It would basically be a struct with two ints in the simplest case, but to support user types as indexes it would need to be template-like, i.e. slice!(type). A slice would look basically like struct slice(T=int) { T lo,hi; } It could also have a .step property. With the above, lo and hi would have to be of the same type, but really it makes sense to let them differ, so slice!(T1,T2). For a range with stepsize, slice!(Tlo,Thi,Tstep). To make writing opSlice methods sane, a single number like the p above should be converted to a slice also. So all arguments passed to opSlice would be of type slice, and in the simple case of integer indices, it would just be: Type opSlice(slice s) { return x[s.lo..s.hi]; } since integers would be the default types for slice. --User Definable '$'-- * A way to specify 'the end' in user types. In the general case the meaning of '$' in a slice cannot be known (because any type can be used as an index), nor can it be simply substituted with something like a .length property, because it may depend on context. Consider a multi-dimensional array class -- A[0..$,3..$] The first $ means one thing, and the second one means another. One solution - make an opLength that gets called with the parameter only context that ever matters in determining the meaning of $.] So in the above int opLength(int i) would get called twice, once with i==0, once with i==1. opLength can be made to return any type if the user just wants it to get 'passed through' to the opSlice call. If you don't need the context you can define it as opLength(). --Step sizes-- This is a handy feature of Python slices. The general syntax for a slice in Python is lo:hi:step, meaning go from 'lo' to 'hi', stepping by 'step' at a time. But any of the 3 components can be left out. lo:hi means step=1. lo::2 means go to the end, stepping by 2. :hi means 0 to hi. Negative steps are also allowed: hi:lo:-1 means go backwards from hi to lo ::-1 go backwards from the last to first element D syntax could be something like lo..hi:step. I like the omission part of Python's syntax. If D had that then most uses of $ would go away since we'd have A[3..] as an alternative to A[3..$]. --bb
Dec 20 2006
Bill Baxter wrote:After trying to write a multi-dimensional array class, my opinion is that D slice support could use some upgrades overall.I'd be very interested in looking at what you've come up with. With my own implementation of a multi-dimensional array type a couple of months ago, I came to the same conclusion. I posted about it in: news://news.digitalmars.com:119/edrv0n$hth$1 digitaldaemon.com http://www.digitalmars.com/d/archives/digitalmars/D/announce/4717.htmlWhat I'd like to see: --MultiRange Slice-- * A way to have multiple ranges in a slice, and a mix slice of and non-slice indices: A[i..j, k..m] A[i..j, p, k..m](snip)A[0..$,3..$]Yes, I would too. It is quite frustrating having the syntax in the language but not being allowed to utilize it... :) I work around this by instead using a custom slice syntax instead: A[range(i,j), range(k,m)] A[range(i,j), p, range(k,m)] A[range(0,end), range(3..end)] A[end-1, p % end] Basicly, the transformation is: $ => end a..b => range(a,b) I briefly described this in: news://news.digitalmars.com:119/eft9id$2aq3$1 digitaldaemon.com The resulting code becomes quite optimal without the need for a position dependent opLength type of operator, but handling all the cases puts a larger burden on the implementor of opIndex.The problem is that opSlice has to look like opSlice(T1 lo, T2 hi) right now -- just two parameters (or zero).[snip]Another solution is a built-in slice type. Ranges like a..b would get converted to slice instances automatically.Yes, this would be my suggestion too. Adding an opApply to one such built in range type would also have the nice side effect of allowing the syntactical sugar: foreach(i; 5..10)--User Definable '$'--[snip]One solution - make an opLength that gets called with the parameter number in which the $ appears.Yes, that is probably the cleanest solution. And if no such opLength(int) overload exists, return the result of opLength() (or possibly .length) /Oskar
Dec 21 2006
Oskar Linde wrote:Bill Baxter wrote:Yeh, that's similar to what I'm doing too. But it's pretty ugly. So I guess that means you're using opIndex for everything and leaving opSlice alone. Are you able to have ranges return arrays and specific indexes return scalar values that way? That seems to me a big reason for having opSlice exist in the first place. The .. in the brackets not only means you're slicing, it also means the function should return another array, versus returning an element. That seems like a nice distinction to have to me.After trying to write a multi-dimensional array class, my opinion is that D slice support could use some upgrades overall.I'd be very interested in looking at what you've come up with. With my own implementation of a multi-dimensional array type a couple of months ago, I came to the same conclusion. I posted about it in: news://news.digitalmars.com:119/edrv0n$hth$1 digitaldaemon.com http://www.digitalmars.com/d/archives/digitalmars/D/announce/4717.htmlWhat I'd like to see: --MultiRange Slice-- * A way to have multiple ranges in a slice, and a mix slice of and non-slice indices: A[i..j, k..m] A[i..j, p, k..m](snip) > A[0..$,3..$] Yes, I would too. It is quite frustrating having the syntax in the language but not being allowed to utilize it... :) I work around this by instead using a custom slice syntax instead: A[range(i,j), range(k,m)] A[range(i,j), p, range(k,m)] A[range(0,end), range(3..end)] A[end-1, p % end]Basicly, the transformation is: $ => end a..b => range(a,b) I briefly described this in: news://news.digitalmars.com:119/eft9id$2aq3$1 digitaldaemon.comThanks for the link. The 'end' thing isn't so bad, at least for a former Matlab user. :-) --bb
Dec 24 2006
Bill Baxter wrote:Oskar Linde wrote:Is there anything particularly wrong with having foo[a..b,c,d..$] being syntactical sugar for foo[a..b][c][d..$] ? That way, I would imagine you could implement slicing and indexing of multidimensional arrays quite easily because, as Norbert Nemec said, indexing just returns an array with dimension reduced by one, and slicing returns an array of the same dimension, but perhaps different size. It also seems to allow any combination of slicing and indexing without needing variadic functions.Bill Baxter wrote:Yeh, that's similar to what I'm doing too. But it's pretty ugly. So I guess that means you're using opIndex for everything and leaving opSlice alone. Are you able to have ranges return arrays and specific indexes return scalar values that way? That seems to me a big reason for having opSlice exist in the first place. The .. in the brackets not only means you're slicing, it also means the function should return another array, versus returning an element. That seems like a nice distinction to have to me.After trying to write a multi-dimensional array class, my opinion is that D slice support could use some upgrades overall.I'd be very interested in looking at what you've come up with. With my own implementation of a multi-dimensional array type a couple of months ago, I came to the same conclusion. I posted about it in: news://news.digitalmars.com:119/edrv0n$hth$1 digitaldaemon.com http://www.digitalmars.com/d/archives/digitalmars/D/announce/4717.htmlWhat I'd like to see: --MultiRange Slice-- * A way to have multiple ranges in a slice, and a mix slice of and non-slice indices: A[i..j, k..m] A[i..j, p, k..m](snip) > A[0..$,3..$] Yes, I would too. It is quite frustrating having the syntax in the language but not being allowed to utilize it... :) I work around this by instead using a custom slice syntax instead: A[range(i,j), range(k,m)] A[range(i,j), p, range(k,m)] A[range(0,end), range(3..end)] A[end-1, p % end]
Dec 24 2006
Reiner Pope wrote:Bill Baxter wrote:Generally speaking, indexing isn't free. And doing it three times is 3x more expensive than doing it once. At the very least if the thing being indexed is a class then a new instance of that class has to be created for each index op. But likely there's also some internal state that has to be copied and adjusted as well. And one expects that indexing operations will often appear in inner loops, so they should be as fast as possible. However, if you use some sort of proxy structs to represent the intermediate indexing expressions and only do the indexing when it's really needed, it may be ok to use [][][]. Basically it's expression templates all over again, just here the expressions are limited to indexing or slice operations. That may work, and it may even be as efficient as a real multi-index slice with compiler optimizations, but I think it will result in code that's far less clear and probably not as fast. If it does pan out, though, then there are certainly advantages as you say to only having to worry about two cases ever -- single index and slice index. Not the any-possible-combination-of-index-and-slice, which admittedly requires some slick vararg template trickery itself. Anyway, I plan to try implementing that soon (with all the brackets, naturally since the syntactic sugar you mention currently doesn't exist). --bbOskar Linde wrote:Is there anything particularly wrong with having foo[a..b,c,d..$] being syntactical sugar for foo[a..b][c][d..$] ? That way, I would imagine you could implement slicing and indexing of multidimensional arrays quite easily because, as Norbert Nemec said, indexing just returns an array with dimension reduced by one, and slicing returns an array of the same dimension, but perhaps different size. It also seems to allow any combination of slicing and indexing without needing variadic functions.Bill Baxter wrote:Yeh, that's similar to what I'm doing too. But it's pretty ugly. So I guess that means you're using opIndex for everything and leaving opSlice alone. Are you able to have ranges return arrays and specific indexes return scalar values that way? That seems to me a big reason for having opSlice exist in the first place. The .. in the brackets not only means you're slicing, it also means the function should return another array, versus returning an element. That seems like a nice distinction to have to me.After trying to write a multi-dimensional array class, my opinion is that D slice support could use some upgrades overall.I'd be very interested in looking at what you've come up with. With my own implementation of a multi-dimensional array type a couple of months ago, I came to the same conclusion. I posted about it in: news://news.digitalmars.com:119/edrv0n$hth$1 digitaldaemon.com http://www.digitalmars.com/d/archives/digitalmars/D/announce/4717.htmlWhat I'd like to see: --MultiRange Slice-- * A way to have multiple ranges in a slice, and a mix slice of and non-slice indices: A[i..j, k..m] A[i..j, p, k..m](snip) > A[0..$,3..$] Yes, I would too. It is quite frustrating having the syntax in the language but not being allowed to utilize it... :) I work around this by instead using a custom slice syntax instead: A[range(i,j), range(k,m)] A[range(i,j), p, range(k,m)] A[range(0,end), range(3..end)] A[end-1, p % end]
Dec 24 2006
== Quote from Reiner Pope (xxxxxx xxx.xx)'s articleIs there anything particularly wrong with having foo[a..b,c,d..$] being syntactical sugar for foo[a..b][c][d..$] ? That way, I would imagine you could implement slicing and indexing of multidimensional arrays quite easily because, as Norbert Nemec said, indexing just returns an array with dimension reduced by one, and slicing returns an array of the same dimension, but perhaps different size. It also seems to allow any combination of slicing and indexing without needing variadic functions.It is a neat idea that unfortunately doesn't work. foo[a..b,c..d] means slice from a to b along dimension 1 and from c to d along dimension 2. foo[a..b] would therefore return a 2-dimensional slice (same dimensionality as foo), and (foo[a..b])[c..d] would slice the same dimension again. Unless, of course, foo[a..b] would return some kind of proxy object that kept track of the last sliced dimension. /Oskar
Dec 26 2006
== Quote from Bill Baxter (dnewsgroup billbaxter.com)'s articleOskar Linde wrote:Yes. Apart from the no-argument opSlice and opSliceAssign, that I overload with the same meaning as for the built in arrays. opSlice is not designed to support more than one dimension.Bill Baxter wrote:Yeh, that's similar to what I'm doing too. But it's pretty ugly. So I guess that means you're using opIndex for everything and leaving opSlice alone.After trying to write a multi-dimensional array class, my opinion is that D slice support could use some upgrades overall.I'd be very interested in looking at what you've come up with. With my own implementation of a multi-dimensional array type a couple of months ago, I came to the same conclusion. I posted about it in: news://news.digitalmars.com:119/edrv0n$hth$1 digitaldaemon.com http://www.digitalmars.com/d/archives/digitalmars/D/announce/4717.htmlWhat I'd like to see:--MultiRange Slice--* A way to have multiple ranges in a slice, and a mix slice of and non-slice indices: A[i..j, k..m] A[i..j, p, k..m](snip)A[0..$,3..$]Yes, I would too. It is quite frustrating having the syntax in the language but not being allowed to utilize it... :) I work around this by instead using a custom slice syntax instead: A[range(i,j), range(k,m)] A[range(i,j), p, range(k,m)] A[range(0,end), range(3..end)] A[end-1, p % end]Are you able to have ranges return arrays and specific indexes return scalar values that way?Yes.That seems to me a big reason for having opSlice exist in the first place. The .. in the brackets not only means you're slicing, it also means the function should return another array, versus returning an element. That seems like a nice distinction to have to me.Yes, definitely. My implementation mimics the behavior of the D native array/slice type (T[]), but extended for multiple dimensions and strides. So given Array!(2,int) A, A[all,5] and A[5,all] are both Array!(1,int) of the 6-th row and column of A. A[5,5] is an int. Some other neat features made possible by strided arrays are for instance A.diag, that returns the 1-dimensional diagonal of the array, so eg: A[] = 0; A.diag[] = 1; would construct the unit matrix.I wish I too could call myself a _former_ such. :) /OskarBasicly, the transformation is: $ => end a..b => range(a,b) I briefly described this in: news://news.digitalmars.com:119/eft9id$2aq3$1 digitaldaemon.comThanks for the link. The 'end' thing isn't so bad, at least for a former Matlab user. :-)
Dec 26 2006
Oskar Linde wrote:== Quote from Bill Baxter (dnewsgroup billbaxter.com)'s articleSounds great. Is your code available anywhere? --bbOskar Linde wrote:Yes. Apart from the no-argument opSlice and opSliceAssign, that I overload with the same meaning as for the built in arrays. opSlice is not designed to support more than one dimension.Bill Baxter wrote:Yeh, that's similar to what I'm doing too. But it's pretty ugly. So I guess that means you're using opIndex for everything and leaving opSlice alone.After trying to write a multi-dimensional array class, my opinion is that D slice support could use some upgrades overall.I'd be very interested in looking at what you've come up with. With my own implementation of a multi-dimensional array type a couple of months ago, I came to the same conclusion. I posted about it in: news://news.digitalmars.com:119/edrv0n$hth$1 digitaldaemon.com http://www.digitalmars.com/d/archives/digitalmars/D/announce/4717.htmlWhat I'd like to see:--MultiRange Slice--* A way to have multiple ranges in a slice, and a mix slice of and non-slice indices: A[i..j, k..m] A[i..j, p, k..m](snip)A[0..$,3..$]Yes, I would too. It is quite frustrating having the syntax in the language but not being allowed to utilize it... :) I work around this by instead using a custom slice syntax instead: A[range(i,j), range(k,m)] A[range(i,j), p, range(k,m)] A[range(0,end), range(3..end)] A[end-1, p % end]Are you able to have ranges return arrays and specific indexes return scalar values that way?Yes.That seems to me a big reason for having opSlice exist in the first place. The .. in the brackets not only means you're slicing, it also means the function should return another array, versus returning an element. That seems like a nice distinction to have to me.Yes, definitely. My implementation mimics the behavior of the D native array/slice type (T[]), but extended for multiple dimensions and strides. So given Array!(2,int) A, A[all,5] and A[5,all] are both Array!(1,int) of the 6-th row and column of A. A[5,5] is an int. Some other neat features made possible by strided arrays are for instance A.diag, that returns the 1-dimensional diagonal of the array, so eg: A[] = 0; A.diag[] = 1; would construct the unit matrix.
Dec 26 2006
== Quote from Andrei Alexandrescu (See Website For Email) (SeeWebsiteForEmail erdani.org)'s articleDon Clugston wrote:I guess the question is, what is the best alternative. I agree about 'length', and I usually don't use "length" in this way, but I do things like x[$-2..$] all the time. Some proposals: 1. Symbols Going in the symbol direction, it might also make sense to *add* something like "^" for the start of a container. This would be useful with AAs and user defined types. We could use both: a[^+2..$-2]. This would only really be useful with containers that did not index from 0, i.e. non-integer or AA indices. char[char[]] words; words[^.."brink"]; // all words in dictionary before 'brink' words["brack"..$] // instead of symbols Which could translate to: words.opSlice(words.opBegin(), "brink") words.opSlice("brack", opEnd()) 2. I like this better: I call it "with without with" In order to maximize the dollar value :) of syntax symbol real estate, the meaning of $ could be expanded as follows: Something like X[$begin..$end] could be a shortcut for either X[0..X.length] for arrays, or X[X.opBegin()..X.opEnd()] for user types. I think the above solves the problem, doesn't it? The "$end" phrase is terse enough for most coders, unique enough to avoid namespace conflicts, avoids the problem of keywords ghosting in and out of existence in mid-expression, and We can stop right there... or go on to something for post-1.0: Other applications of $ could be: A. Syntax reduction for enumerated types and fields: struct Colors { enum { red, green, blue }; void set(int c); }; Colors c; c.set($red); This use of enumerated type is becoming more common, having "$" be a shortcut for <context>.X might make a lot of code more readable. The question then becomes, "Which contexts are searched for .X?" B. Reserved for language features. Leave this open for language designer use. All $xyz expressions are context dependent keywords. This allows much shorter words to be used, and allows language features to be named intelligently without worrying about crashing into user-defined names. For example, C could never introduce a new keyword called "begin" or "end", since it would break nearly every C program, but we can easily add a keyword called $begin which will not conflict with anything, since the $ saves us from conflicts. Most of the discussions for new features here have at least some arguments on how to add the new syntax for the feature, what other uses those symbols could be used for, etc. The $xyz route allows Walter to introduce lots of language concepts in the future without conflicts. It could even be used to prototype keywords that are experimental. They can even be removed or promoted to non-$ status later if desired. "#line" and "#file" quasi-keywords.Andrei Alexandrescu (See Website For Email) wrote:It would indeed break an enormous amount of code, but "all costs" includes "enormous costs". :o) A reasonable migration path is to deprecate them soon and make them illegal over the course of one year. A small book could be written on just how bad language design is using "length" and "$" to capture slice size inside a slice expression. I managed to write two lengthy emails to Walter about them, and just barely got started. Long story short, "length" introduces a keyword through the back door, effectively making any use of "length" anywhere unrecommended and highly fragile. Using "$" is a waste of symbolic real estate to serve a narrow purpose; the semantics isn't naturally generalized to its logical conclusion; and the choice of symbol itself been vastly better as it has count connotation in natural language, and making it into an operator would have fixed the generalization issue). As things stand now, the rules governing the popping up of "length" and "$" constitute a sudden boo-boo on an otherwise carefully designed expression landscape.Similarly, let's say that a group of revolutionaries convinces Walter (as I understand happened in case of using "length" and "$" inside slice expressions, which is a shame and an absolute disaster that must be undone at all costs) to implement "auto"This off-hand remark worries me. I presume that you mean being able to reference the length of a string, from inside the slice? (rather than simply the notation). And the problem being that it requires a sliceable entity to know its length? Or is the problem more serious than that? It's worrying because any change would break an enormous amount of code.I like the terseness of "$" but I'm willing to do away with it if it really is that bad. What I'm wondering, is how far do you think we need to roll back the syntax, before it's "The Right Thing" (tm) again? Do we really need to go all the way to myarray[0..myarray.length], or can some intermediate solution work? KevinThese issues you're raising seem to be far too fundamental to be fixed in the next few days, casting grave doubts on whether a D1.0 release on Jan 1 is a good idea.The lvalue/rvalue issue is fundamental. I'm not in the position to assess whether it's a maker or breaker of D 1.0. The "length"/"$" issue is not fundamental the same way that C's declaration syntax, Java's throw specifications, C++'s use of "<" and ">" for templates, and Mao Zedong's refusal to use a toothbrush are not fundamental. It will "just" go down in history as a huge embarrassment and a good resource for cheap shooters and naysayers. If I understand its genesis, it will also be a canonical example of why design by committee is bad. Andrei
Dec 21 2006
On Wed, 20 Dec 2006 06:24:28 -0800, "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> wrote:Long story short, "length" introduces a keyword through the back door, effectively making any use of "length" anywhere unrecommended and highly fragile.I've always had a strong feeling that all-lowercase words should be reserved as potential future keywords anyway, barring a few special cases like 'i'. I suppose a lot of that comes down to style, though. If you have a convention where most identifiers start with a capital (excluding prefixes like 'l_', 'p_' or 'm_'), that only really leaves common short non-descriptive names like 'i' as a special case. It's one of the things that bugs me about the normal Java style - it allows all-lowercase identifiers (only using capitals as word separators), which look like potential keywords if you're not too familiar with Java. Besides, from natural language convention, capitals should appear at the start of a sentence - having them in the middle of an sentence-like identifier but not at the start just looks wrong to me. -- Remove 'wants' and 'nospam' from e-mail.
Dec 22 2006
Andrei Alexandrescu (See Website For Email) wrote:Let me illustrate further why ident is important and what solution we should have for it. Consider C's response to ident: #define IDENT(e) (e) ... ...leading to the following implementation of ident: auto ident(auto x) { return x; }I don't get it. Why is it necessary (or even desirable) for functions to return lvalues? I can see how it'd be an interesting trick, and I can appreciate the experimental curiosity about how the language (and the implementation) should cope with the explicit handling of lvalues. But I can't think of a real-world use case. Are there languages where this is currently possible? How do they implement it? And, much more importantly, what do people use it for? --benji
Dec 20 2006
Benji Smith wrote:Andrei Alexandrescu (See Website For Email) wrote:For functions, I essentially agree. There aren't many use cases for it, and for the few niche ones a pointer will often suffice. But for methods of classes on the other hand, it can be valuable at times.Let me illustrate further why ident is important and what solution we should have for it. Consider C's response to ident: #define IDENT(e) (e)> ... >...leading to the following implementation of ident: auto ident(auto x) { return x; }I don't get it. Why is it necessary (or even desirable) for functions to return lvalues?Are there languages where this is currently possible?C++, by returning a referance. -- Chris Nicholson-Sauls
Dec 20 2006
Chris Nicholson-Sauls wrote:Benji Smith wrote:Perl 5 too. AndreiAre there languages where this is currently possible?C++, by returning a referance.
Dec 21 2006
Benji Smith wrote:Andrei Alexandrescu (See Website For Email) wrote:Methods might want to return lvalues, but indeed the need is not overwhelming. (They could return pointers after all.) But the point is different. You want to have a grip on all types, and ident shows that you can't. For example, in current D you can't (barring a hack that I saw in a post around here) have a template that takes a function and creates one of the exact signature. That is a vastly useful and desirable thing to want; think e.g. of a function that memoizes any other function. AndreiLet me illustrate further why ident is important and what solution we should have for it. Consider C's response to ident: #define IDENT(e) (e)> ... >...leading to the following implementation of ident: auto ident(auto x) { return x; }I don't get it. Why is it necessary (or even desirable) for functions to return lvalues?
Dec 21 2006
Benji Smith wrote:Andrei Alexandrescu (See Website For Email) wrote:FWIW, In PL/1 the substring function could be used as an lvalue. (Analogously, in D one can use array slices as lvalues.) I can't remember whether PL/1 allowed one to use this feature to delete characters, or only to alter them, but in Python one can use this feature to delete characters (or replace them with something that isn't a character?).Let me illustrate further why ident is important and what solution we should have for it. Consider C's response to ident: #define IDENT(e) (e)> ... >...leading to the following implementation of ident: auto ident(auto x) { return x; }I don't get it. Why is it necessary (or even desirable) for functions to return lvalues? I can see how it'd be an interesting trick, and I can appreciate the experimental curiosity about how the language (and the implementation) should cope with the explicit handling of lvalues. But I can't think of a real-world use case. Are there languages where this is currently possible? How do they implement it? And, much more importantly, what do people use it for? --benji
Dec 21 2006
Benji Smith wrote:Andrei Alexandrescu (See Website For Email) wrote:There have been numerous cases here in the NG of people griping with this problem when using property-methods, or operator overloads such as opIndex, which both cannot return lvalues and thus have usage limitations. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#DLet me illustrate further why ident is important and what solution we should have for it. Consider C's response to ident: #define IDENT(e) (e)> ... >...leading to the following implementation of ident: auto ident(auto x) { return x; }I don't get it. Why is it necessary (or even desirable) for functions to return lvalues? I can see how it'd be an interesting trick, and I can appreciate the experimental curiosity about how the language (and the implementation) should cope with the explicit handling of lvalues. But I can't think of a real-world use case. Are there languages where this is currently possible? How do they implement it? And, much more importantly, what do people use it for? --benji
Dec 24 2006
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Andrei Alexandrescu (See Website for Email) schrieb am 2006-12-20:Kevin Bealer wrote:<snip>== Quote from Andrei Alexandrescu (See Website For Email) ...That remains to be seen, but I think the buck stops at functions. The problem of duplicating templates for inout (lvalues) and rvalues stays, but I have an idea about that, that I might tell about tomorrow.We then discussed another solution that I won't bore you with, as it's so wrong it hurts. My current thoughts navigate around two possible solutions. One is to make the storage part of the template parameters: template ident(S T) { S T ident(S T e) { return e; } } When two adjacent symbols appear in a template parameter list, they unambiguously denote a storage class followed by a type. So "S" can bind to things like "in", "inout" etc., while "T" can bind to types.Unambiguously? template Templ_1(int i) { } Is "int" now a type or a storage class?Another solution that works is to commit to the idea of associating the storage class with the parameter (and divorce it from the type entirely). In that case, the following syntax describes what happens: template ident(T) { storageof(e) T ident(storageof(e) T e) { return e; } } The storageof(symbol) meta-operator yields the storage of that symbol.This seems to be much cleaner. How would storageof handle the following: mixin ident!(extern(C) inout float function(int)) foo; Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFFiUIQLK5blCcjpWoRAo38AJwNP1gCk52kIKuto9x76paeQq1LewCfUbrK zg3KJGIRH5wVoe/JeSR1aHE= =0HhD -----END PGP SIGNATURE-----
Dec 20 2006
Thomas Kuehne wrote:It's a type because the symbol "int" is already bound as a keyword. There's no way (to the best of my knowledge) to specify two adjacent non-keyword symbols in a template parameter list.We then discussed another solution that I won't bore you with, as it's so wrong it hurts. My current thoughts navigate around two possible solutions. One is to make the storage part of the template parameters: template ident(S T) { S T ident(S T e) { return e; } } When two adjacent symbols appear in a template parameter list, they unambiguously denote a storage class followed by a type. So "S" can bind to things like "in", "inout" etc., while "T" can bind to types.Unambiguously? template Templ_1(int i) { } Is "int" now a type or a storage class?The more I think of it, the more I think it sucks :o).Another solution that works is to commit to the idea of associating the storage class with the parameter (and divorce it from the type entirely). In that case, the following syntax describes what happens: template ident(T) { storageof(e) T ident(storageof(e) T e) { return e; } } The storageof(symbol) meta-operator yields the storage of that symbol.This seems to be much cleaner.How would storageof handle the following: mixin ident!(extern(C) inout float function(int)) foo;C does not have lvalue return types. Andrei
Dec 20 2006
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Andrei Alexandrescu (See Website For Email) schrieb am 2006-12-20:Thomas Kuehne wrote:enum S{ FOO } template Templ(S T) { } mixin Templ!(S.FOO) bar; Do you consider S an keyword here? Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFFiWtKLK5blCcjpWoRAnRTAKCLuXgE7qH/OyabfPJ3aGaPRT0IaQCeLFy2 3PDyDfn8rcPDXpKq+m572HQ= =l6HF -----END PGP SIGNATURE-----It's a type because the symbol "int" is already bound as a keyword. There's no way (to the best of my knowledge) to specify two adjacent non-keyword symbols in a template parameter list.We then discussed another solution that I won't bore you with, as it's so wrong it hurts. My current thoughts navigate around two possible solutions. One is to make the storage part of the template parameters: template ident(S T) { S T ident(S T e) { return e; } } When two adjacent symbols appear in a template parameter list, they unambiguously denote a storage class followed by a type. So "S" can bind to things like "in", "inout" etc., while "T" can bind to types.Unambiguously? template Templ_1(int i) { } Is "int" now a type or a storage class?
Dec 20 2006
Thomas Kuehne wrote:enum S{ FOO } template Templ(S T) { } mixin Templ!(S.FOO) bar; Do you consider S an keyword here?You're right, it makes parsing dependent on the symbol table, breaking a nice property of D. Back to the drawing board. Andrei
Dec 21 2006
Ohh, why didn't you start a new thread... I hate it when a thread gets so nested that one needs a widescreen monitor to view the topic list. :P Andrei Alexandrescu (See Website for Email) wrote:Kevin Bealer wrote:Hum, I wonder if the 'lazy' "storage class" (it's not really a storage class per se) is something that is worth, or even makes sense preserving. If you want to completely preserve an expression, then what are the properties/characteristics of that expression? The type is one property of course, then there is if the expression is an rvalue or lvalue, and also if it is const/final/readonly or something like that. But 'lazy' is not something that is a property/characteristic of an expression is it? It is a concept that exists only in parameter passing of function calls. What would be an ident function trying to "preserve" the lazyness of an expression?..== Quote from Andrei Alexandrescu (See Website For Email) ...I had to leave to Romania before having the time to post thoughts on the lvalue/rvalue discussion I've had with Walter on Saturday. We both agree that it's a serious problem with D's type system that needs fixing (and that he needs to do all the work :o)). I've continued to think of the issue, at least enough to figure that the solution we initially thought of is not sound. Walter is reluctant to offering the ability to overload on lvalue/rvalue, while it turns out that that can't be avoided. Let's return to my litmus test - the identity function ident(e), which can snuggle any expression e and leave its semantics unchanged. My thesis is that this function is an important test of a language's power. The starting point would be: template ident(T) { T ident(T e) { return e; } inout T ident(inout T e) { return e; } } The problem with this approach is that it doesn't scale. Right now "inout" is about the only interesting storage class of a function parameter, but "lazy" comes to mind (which I hope to get rid of soon via a much better solution) and later on we'll have "const", and each of these combinations will mean one more duplication of the ident body (and, by extension, of any function that wants to just pass the storage class outside). Walter had an idea along the line:That remains to be seen, but I think the buck stops at functions. The problem of duplicating templates for inout (lvalues) and rvalues stays, but I have an idea about that, that I might tell about tomorrow. AndreiDid you ever work out how to do this lvalue/rvalue idea?template ident(T) { return T ident(return T e) { return e; } } which allows you to reuse the return keyword as a symbolic placeholder for passing out the storage class. This solution is severely shortsighted in that it fixes ident and only ident, whereas the purpose of ident is to serve as a simplified case for functions with multiple parameters. So this fell as well. We then discussed another solution that I won't bore you with, as it's so wrong it hurts. My current thoughts navigate around two possible solutions. One is to make the storage part of the template parameters: template ident(S T) { S T ident(S T e) { return e; } } When two adjacent symbols appear in a template parameter list, they unambiguously denote a storage class followed by a type. So "S" can bind to things like "in", "inout" etc., while "T" can bind to types. In the example above, the compiler will deduce both S and T from the argument type. It already does that, so that's no extra difficulty. The key point that makes this scale is that you can bind S and T multiple times in a variadic template. Another interesting detail is that it clarifies that you can't solve the problem without somehow compiling two versions of the ident function. So in the end overloading on "inout" is a must. Another solution that works is to commit to the idea of associating the storage class with the parameter (and divorce it from the type entirely). In that case, the following syntax describes what happens: template ident(T) { storageof(e) T ident(storageof(e) T e) { return e; } } The storageof(symbol) meta-operator yields the storage of that symbol. The problem with this notation is that it uses a symbol without having seen it. That's not too bad (it already happens due to the way symbols at global scope are looked up) but in this case it does have a fishy smell. Another thing that I don't like it that the code obscures what's going on - namely that one ident will be generated for each storage class, even though that's not reflected in the parameter type list. Finally, one related but slightly different topic is the necessity of deduced return types for functions, e.g. by using "auto" to denote the return type. Automatic deduction of return types is very useful in that it allows compact template function definition - no more need for a template that defines a homonym function. With deduced argument types, ident can be written as: auto ident(S T)(S T e) { return e; } which is, I think, the Platonic ideal of ident as far as expressing it in D goes. AndreiHum, interesting. I've seen this issue come up in another context as well, namely a paper about a proposal (Javari) for the addition of a reference immutability construct to Java, in the form of a 'const'-like keyword called 'readonly'. Since 'readonly' is very like (the future) 'inout', as they are both "type modifiers" of sorts, with similar syntax, they too had the issue of trying to avoid function definition duplication where only the absence or presence of 'readonly' changed in the prototype. Their solution was similar to the above. But instead of a "storage class" modifier variable like S, they used a keyword ('romaybe'), so that a templated function would be defined as this: romaybe Object getValue(romaybe thisojb) { return thisojb.value; } Which would generate two function instances, one where 'romaybe' is substituted by 'readonly' and other where it is substituted with nothing (which is mutability). The difference between the "storage class" modifier variable syntax is that only one "storage class" [*] can be parameterized, i.e., you can't have S1, S2, etc. [*] we really should not be calling this, "storage class". -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Dec 26 2006
Lionello Lunesu wrote:Walter Bright wrote:There is no *extra* copying, in the sense that exactly one copy is done. But most of the time the copy obtained is not used, in which case that one copy is spurious. AndreiIt is possible to force the return type of opAssign. But I'd suggest making it an S*, with the rewrite to: *(a.opAssign(b))class C { C* opAssign(...) {...} } struct S { S* opAssign(...) {...} } Seriously?? Ugh. Why not just "C opAssign" and "S opAssign"? In the opCall discussion you said yourself that the return value will be optimized. Am I missing something?
Dec 14 2006
Andrei Alexandrescu (See Website for Email) wrote:Lionello Lunesu wrote:I'd think that the optimizer would not only eliminate the extra copy, but the entire construction of the return value .. No? L.Walter Bright wrote:There is no *extra* copying, in the sense that exactly one copy is done. But most of the time the copy obtained is not used, in which case that one copy is spurious.It is possible to force the return type of opAssign. But I'd suggest making it an S*, with the rewrite to: *(a.opAssign(b))class C { C* opAssign(...) {...} } struct S { S* opAssign(...) {...} } Seriously?? Ugh. Why not just "C opAssign" and "S opAssign"? In the opCall discussion you said yourself that the return value will be optimized. Am I missing something?
Dec 15 2006
Lionello Lunesu wrote:Andrei Alexandrescu (See Website for Email) wrote:If the routine is inlined perhaps, but otherwise not. SeanLionello Lunesu wrote:I'd think that the optimizer would not only eliminate the extra copy, but the entire construction of the return value .. No?Walter Bright wrote:There is no *extra* copying, in the sense that exactly one copy is done. But most of the time the copy obtained is not used, in which case that one copy is spurious.It is possible to force the return type of opAssign. But I'd suggest making it an S*, with the rewrite to: *(a.opAssign(b))class C { C* opAssign(...) {...} } struct S { S* opAssign(...) {...} } Seriously?? Ugh. Why not just "C opAssign" and "S opAssign"? In the opCall discussion you said yourself that the return value will be optimized. Am I missing something?
Dec 15 2006
Walter Bright wrote:Andrei Alexandrescu (See Website For Email) wrote:Makes sense, but then I keep on racking my brain to ever think of any C++ code that ever return something else than *this. Never saw any, never wrote any - ever. So I just conclude that customizing =, +=, -= etc. is a good thing, but customizing their return type and value is not. Adding the silly "return this;" litany at the end of every single operator is not something for a language that wants to do things the right way. The speed will be better, too. It's kind of annoying to know that I can't write: a = b; and simply have the compiler do what it takes and no more. Every of those spurious copies is just some more discomfort I have to put in. AndreiRequire opAssign() to always return void and have the compiler return a reference to the left-hand side. That is, transform: a = b; into: (a.opAssign(b), a); with the mention that a only gets evaluated once. This way assignment _always_ returns its left-hand side and user code cannot subvert that behavior. Also the code will be efficient because no more spurious copies are being made.It is possible to force the return type of opAssign. But I'd suggest making it an S*, with the rewrite to: *(a.opAssign(b))
Dec 14 2006
== Quote from Andrei Alexandrescu (See Website For Email) (SeeWebsiteForEmail erdani.org)'s articleWalter Bright wrote:I wonder, are there cases where assignment should not return 'this'? What I'm thinking of is something like: class StringExpression { ... holds equations between strings }; class String { // wrapper around char[] StringExpression opCat(String x); StringExpression opCat(StringExpression x); }; This 'expression-class' stuff is done more commonly with matrices, where the programmer is trying to to combine multiplies and adds via some optimization rules. Since C++ at least is flexible (syntax wise at least) on operator inputs and outputs, I have to wonder if there are really consequences for not following those expectations here. Imagine a string expression like this: a = b ~ (c = d ~ e); Now if I can do "StringExpression String::opAssign(...)", this could maybe be rewritten (code not shown...) to result in something like this: a = b ~ d ~ e; c = a[b.length..a.length]; Not that this is worth the effort or that the language should do this with strings, but as a programmer, can I reasonably do this kind of trick with operator return values or am I just digging a hole for myself? (I realize that even if useful it may not be worthwhile.) KevinAndrei Alexandrescu (See Website for Email) wrote:I guess compiling internally two versions, one that returns S and one that returns void, might be worth looking into. Because right now user code for opAssign() can't be politically correct and efficient at the same time. Here's a better alternative: Require opAssign() to always return void and have the compiler return a reference to the left-hand side. That is, transform: a = b; into: (a.opAssign(b), a); with the mention that a only gets evaluated once. This way assignment _always_ returns its left-hand side and user code cannot subvert that behavior. Also the code will be efficient because no more spurious copies are being made. AndreiThat makes me wonder - if a = b is used without picking up its result (the usual case), and if opAssign() returns an S, will the compiler optimize away the extra copy at zero cost?No. The caller and callee don't know about each other.What I think happens is that the function will take a pointer to the destination which is zero, so a comparison will be made. But perhaps the optimizer will take care of eliminating that?You could have a null pointer passed to the return result, but that would entail an extra check on the part of the callee. This is why a lot of C++ code tends to return references instead of values.
Dec 14 2006
Kevin Bealer wrote:== Quote from Andrei Alexandrescu (See Website For Email) (SeeWebsiteForEmail erdani.org)'s articleI've been thinking the same thing, and so far haven't been able to come up with an example where "correct" use of an assignment operator should not return 'this'. I was considering suggestion the same thing as Andrei above, but was hoping someone would post a counterexample in the interim :-) About the only weird thing with returning void and allowing this: a = b = c; Is that it isn't equivalent to this: a.opAssign( b.opAssign( c ) ); Which may be a bit confusing. But it does seem quite natural that the result of an assignment should always be the LHS of the assignment. SeanHere's a better alternative: Require opAssign() to always return void and have the compiler return a reference to the left-hand side. That is, transform: a = b; into: (a.opAssign(b), a); with the mention that a only gets evaluated once. This way assignment _always_ returns its left-hand side and user code cannot subvert that behavior. Also the code will be efficient because no more spurious copies are being made.I wonder, are there cases where assignment should not return 'this'?
Dec 14 2006
Walter Bright wrote:Stewart Gordon wrote:<snip>C'mon, what's your use case for being allowed to return something other than the new value of a from the expression (a = b)? Stewart.The programmer would have a choice - opAssign returning void to modify in-place the object referenced by the lvalue, or returning a new object that will be assigned to the lvalue. What is this precluding?opAssign has 3 externally visible characteristics: 1) the parameter 2) the 'this' pointer 3) the return value Your proposal mixes up 2 and 3. opAssign works like: a = b becomes: a.opAssign(b) The return value is not assigned to a, it is the value of the expression (a = b). Mixing up the return value and the assignment to a will cause problems, as the two are different things, and should be independent.
Dec 14 2006
Stewart Gordon wrote:C'mon, what's your use case for being allowed to return something other than the new value of a from the expression (a = b)?I don't have one. But I prefer to impose as few restrictions as possible, as people keep finding unanticipated cool new things to do. So I'd argue that there should be compelling case put forward to impose such a restriction, rather than wondering what one can do with the flexibility.
Dec 14 2006
Walter Bright wrote:Stewart Gordon wrote:For one, it might be useful to return some sort of proxy for 'a'. --bbC'mon, what's your use case for being allowed to return something other than the new value of a from the expression (a = b)?I don't have one. But I prefer to impose as few restrictions as possible, as people keep finding unanticipated cool new things to do. So I'd argue that there should be compelling case put forward to impose such a restriction, rather than wondering what one can do with the flexibility.
Dec 14 2006
Walter Bright wrote:Stewart Gordon wrote:Just did so in my previous post. AndreiC'mon, what's your use case for being allowed to return something other than the new value of a from the expression (a = b)?I don't have one. But I prefer to impose as few restrictions as possible, as people keep finding unanticipated cool new things to do. So I'd argue that there should be compelling case put forward to impose such a restriction, rather than wondering what one can do with the flexibility.
Dec 14 2006
Walter Bright wrote:Stewart Gordon wrote:We already have what strikes the rest of us as a compelling case: what we've just told you that returning from opAssign _should_ be able to do. Stewart.C'mon, what's your use case for being allowed to return something other than the new value of a from the expression (a = b)?I don't have one. But I prefer to impose as few restrictions as possible, as people keep finding unanticipated cool new things to do. So I'd argue that there should be compelling case put forward to impose such a restriction, rather than wondering what one can do with the flexibility.
Dec 14 2006
Stewart Gordon wrote:We already have what strikes the rest of us as a compelling case: what we've just told you that returning from opAssign _should_ be able to do.That case was trying to make opAssign be a constructor. Since there are other ways to construct it, I don't see why it's a compelling case. What is bought with this that cannot be otherwise done?
Dec 14 2006
Walter Bright wrote:Stewart Gordon wrote:The cases you seemingly pander to for having opAssign in the first place are trying to make opAssign be a property setter. Since there are other ways to set properties, I don't see why this is a compelling case. What is bought with this that cannot be otherwise done? Stewart.We already have what strikes the rest of us as a compelling case: what we've just told you that returning from opAssign _should_ be able to do.That case was trying to make opAssign be a constructor. Since there are other ways to construct it, I don't see why it's a compelling case. What is bought with this that cannot be otherwise done?
Dec 15 2006
char* p =3D new char[32]; Error: cannot implicitly convert expression (new char[](32)) of type = char[] to char* Should this be a special case? Currently it needs (new char[32]).ptr
Dec 09 2006
char *newCharz( uint size ) { return (new char [size]).ptr; } char *p = newCharz(32); :P I like the explicity. Chris Miller wrote:char* p = new char[32]; Error: cannot implicitly convert expression (new char[](32)) of type char[] to char* Should this be a special case? Currently it needs (new char[32]).ptr
Dec 09 2006
On Sat, 09 Dec 2006 11:33:17 -0500, Alexander Panek = <a.panek brainsware.org> wrote:char *newCharz( uint size ) { return (new char [size]).ptr; } char *p =3D newCharz(32); :P I like the explicity.Well, I'm not sure what it should be, but you already made a mistake: it= = should be size_t instead of uint ;) Plus, I never said it was a zero-terminated string.Chris Miller wrote:=char* p =3D new char[32]; Error: cannot implicitly convert expression (new char[](32)) of type=rchar[] to char* Should this be a special case? Currently it needs (new char[32]).pt=
Dec 09 2006
Chris Miller wrote:On Sat, 09 Dec 2006 11:33:17 -0500, Alexander Panek <a.panek brainsware.org> wrote:Oi. Sorry: char * toChars( size_t size ) { return (new char[size]).ptr; } char * toCharz( size_t size ) { return (new char[size + 1]).ptr; } Better? :Pchar *newCharz( uint size ) { return (new char [size]).ptr; } char *p = newCharz(32); :P I like the explicity.Well, I'm not sure what it should be, but you already made a mistake: it should be size_t instead of uint ;) Plus, I never said it was a zero-terminated string.Chris Miller wrote:char* p = new char[32]; Error: cannot implicitly convert expression (new char[](32)) of type char[] to char* Should this be a special case? Currently it needs (new char[32]).ptr
Dec 09 2006
Those are newCharz and newChars, of course. Apart from that, this might be interesting, too: // alias thingie array; template Ptr (T) { // Create a simple thingie T *newPtrs ( size_t size ) { return (new T[size]).ptr; } // Create null-terminated thingie T *newPtrz ( size_t size ) { T *p = (new T[size + 1]).ptr p[$ - 1] = '\0'; return p; } } alias Ptr!(char).newPtrs newChars; alias Ptr!(char).newPtrz newCharz; Not tested, but this might be useful for someone. Alexander Panek wrote:Chris Miller wrote:On Sat, 09 Dec 2006 11:33:17 -0500, Alexander Panek <a.panek brainsware.org> wrote:Oi. Sorry: char * toChars( size_t size ) { return (new char[size]).ptr; } char * toCharz( size_t size ) { return (new char[size + 1]).ptr; } Better? :Pchar *newCharz( uint size ) { return (new char [size]).ptr; } char *p = newCharz(32); :P I like the explicity.Well, I'm not sure what it should be, but you already made a mistake: it should be size_t instead of uint ;) Plus, I never said it was a zero-terminated string.Chris Miller wrote:char* p = new char[32]; Error: cannot implicitly convert expression (new char[](32)) of type char[] to char* Should this be a special case? Currently it needs (new char[32]).ptr
Dec 09 2006
On Sat, 09 Dec 2006 11:53:07 -0500, Alexander Panek = <a.panek brainsware.org> wrote:Those are newCharz and newChars, of course. Apart from that, this might be interesting, too: // alias thingie array; template Ptr (T) { // Create a simple thingie T *newPtrs ( size_t size ) { return (new T[size]).ptr; } // Create null-terminated thingie T *newPtrz ( size_t size ) { T *p =3D (new T[size + 1]).ptr p[$ - 1] =3D '\0'; return p; } } alias Ptr!(char).newPtrs newChars; alias Ptr!(char).newPtrz newCharz; Not tested, but this might be useful for someone.Not bad, perhaps needs better names though. newChars still sounds like n= ew = char[] (array). Maybe allocptr/ptrAlloc/ptralloc/palloc to have a simila= r = name as malloc since they have similarities (working with pointers). = *shrug*
Dec 09 2006
Chris Miller wrote:char* p = new char[32]; Error: cannot implicitly convert expression (new char[](32)) of type char[] to char* Should this be a special case? Currently it needs (new char[32]).ptrWell, the problem is that since this change, char[] is not the same thing as char*; IMO, this is a good thing. You really should have to jump through an extra hoop to get there, because they're not the same. It's no different than a cast, if it is a little harder to type. -- - EricAnderton at yahoo
Dec 11 2006
- Casting a value v to a struct S is now rewritten as S(v). I'm 100% against this. This is what C++ does, and it conflates construction and casting; with some VERY simple examples (such as the first one we think of when we think of additional types, bignums), there's an ambiguous conflict because one of the constructors should have a count of how many digits you want - and one of the casters takes an integer for a value to initialise to. My solution at the time was to add a dummy argument to the constructor so that the compiler didn't try to match them, which is absurd. I don't know why C++ was designed like this when its error was so blatant, but D doesn't need to replicate its mistake. "S.opCastFrom (v)" please, except that it shouldn't be static either.
Dec 09 2006
Burton Radons wrote:- Casting a value v to a struct S is now rewritten as S(v). I'm 100% against this. This is what C++ does, and it conflates construction and casting; with some VERY simple examples (such as the first one we think of when we think of additional types, bignums), there's an ambiguous conflict because one of the constructors should have a count of how many digits you want - and one of the casters takes an integer for a value to initialise to. My solution at the time was to add a dummy argument to the constructor so that the compiler didn't try to match them, which is absurd. I don't know why C++ was designed like this when its error was so blatant, but D doesn't need to replicate its mistake. "S.opCastFrom (v)" please, except that it shouldn't be static either.Essentially agreed. I'm not entirely fond of the "silent" new opAssign either, because of the visual ambiguity it can lead to. (Its worth noting that, even though '='->opAssign is listed in the spec, the bottom of the same page still lists '=' among the operators which will not be given overloads. Hm.) -- Chris Nicholson-Sauls
Dec 09 2006
Chris Nicholson-Sauls wrote:Burton Radons wrote:Same. And frankly, I'm having trouble coming up with a practical use for the new opAssign. For one thing, it isn't commutative: auto c = new MyClass; int i = 5; c = i; // will work i = c; // won't work And now structs have an opAssign but still have binary copy semantics and no ctor/dtor support. The result is totally confusing. What was the use case for this feature? Sean- Casting a value v to a struct S is now rewritten as S(v). I'm 100% against this. This is what C++ does, and it conflates construction and casting; with some VERY simple examples (such as the first one we think of when we think of additional types, bignums), there's an ambiguous conflict because one of the constructors should have a count of how many digits you want - and one of the casters takes an integer for a value to initialise to. My solution at the time was to add a dummy argument to the constructor so that the compiler didn't try to match them, which is absurd. I don't know why C++ was designed like this when its error was so blatant, but D doesn't need to replicate its mistake. "S.opCastFrom (v)" please, except that it shouldn't be static either.Essentially agreed. I'm not entirely fond of the "silent" new opAssign either, because of the visual ambiguity it can lead to. (Its worth noting that, even though '='->opAssign is listed in the spec, the bottom of the same page still lists '=' among the operators which will not be given overloads. Hm.)
Dec 09 2006
Sean Kelly schrieb:Chris Nicholson-Sauls wrote:Why are opAssign overloads possible for structs? They lead to a bad style like the example above: It's possible to assign a primitive type to an aggregate type, without showing what is done in the code.Burton Radons wrote:Same. And frankly, I'm having trouble coming up with a practical use for the new opAssign. For one thing, it isn't commutative: auto c = new MyClass; int i = 5; c = i; // will work i = c; // won't work And now structs have an opAssign but still have binary copy semantics and no ctor/dtor support. The result is totally confusing. What was the use case for this feature? Sean- Casting a value v to a struct S is now rewritten as S(v). I'm 100% against this. This is what C++ does, and it conflates construction and casting; with some VERY simple examples (such as the first one we think of when we think of additional types, bignums), there's an ambiguous conflict because one of the constructors should have a count of how many digits you want - and one of the casters takes an integer for a value to initialise to. My solution at the time was to add a dummy argument to the constructor so that the compiler didn't try to match them, which is absurd. I don't know why C++ was designed like this when its error was so blatant, but D doesn't need to replicate its mistake. "S.opCastFrom (v)" please, except that it shouldn't be static either.Essentially agreed. I'm not entirely fond of the "silent" new opAssign either, because of the visual ambiguity it can lead to. (Its worth noting that, even though '='->opAssign is listed in the spec, the bottom of the same page still lists '=' among the operators which will not be given overloads. Hm.)
Dec 09 2006
On Sat, 09 Dec 2006 02:20:00 -0800, Walter Bright = <newshound digitalmars.com> wrote:More ABI changes, and implicit [] =3D> * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zipum... but this is the link for dmd.175.zip again, not 177. -JJR
Dec 09 2006
Thanks for this. But std.demangle seems to be broken now.
Dec 09 2006
Walter in dmd v0.176 you improved name mangling, but I've found one very confusing change...is it your intension to reuse the "b" which used to mean "bit," to now mean a "bool" instead of "x?" For example, one function "real vdb(real, real, real, real, real, real, bool = false)" used in my financial D dll, before dmd v0.148 was released when the "bool" data type was added (replacing the "bit" data type) the function's mangled name was "D13financial_dll3vdbFeeeeeebZe". And with dmd v0.148 and later, the "bit" data type become an alias to bool and all the "b" (for "bit") were replaced with "x" (for "bool"), thus the funtion's mangled name changed to become "D13financial_dll3vdbFeeeeeexZe". But now it looks like "x" has been replaced by "b," is this a mistake on your part, or have you decided to use a "b" as meaning a "bool" (discarding "x")? So currently the "vdb()" funtion under dmd v0.177 has returned to the old mangled name "D13financial_dll3vdbFeeeeeebZe," as if a "bool" has become a "bit" again. Thanks again for all the great work you've been putting into D year after year! David L. ------------------------------------------------------------------- "Dare to reach for the Stars...Dare to Dream, Build, and Achieve!" ------------------------------------------------------------------- MKoD: http://spottedtiger.tripod.com/D_Language/D_Main_XP.html "Walter Bright" <newshound digitalmars.com> wrote in message news:ele2k9$2hr5$1 digitaldaemon.com...More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zip
Dec 09 2006
David L. Davis wrote:Walter in dmd v0.176 you improved name mangling, but I've found one very confusing change...is it your intension to reuse the "b" which used to mean "bit," to now mean a "bool" instead of "x?"That's what people asked for.For example, one function "real vdb(real, real, real, real, real, real, bool = false)" used in my financial D dll, before dmd v0.148 was released when the "bool" data type was added (replacing the "bit" data type) the function's mangled name was "D13financial_dll3vdbFeeeeeebZe". And with dmd v0.148 and later, the "bit" data type become an alias to bool and all the "b" (for "bit") were replaced with "x" (for "bool"), thus the funtion's mangled name changed to become "D13financial_dll3vdbFeeeeeexZe". But now it looks like "x" has been replaced by "b," is this a mistake on your part, or have you decided to use a "b" as meaning a "bool" (discarding "x")?That's correct.So currently the "vdb()" funtion under dmd v0.177 has returned to the old mangled name "D13financial_dll3vdbFeeeeeebZe," as if a "bool" has become a "bit" again.Yes. There's no going back to bit.Thanks again for all the great work you've been putting into D year after year!You're most welcome.
Dec 09 2006
David L. Davis wrote:Walter in dmd v0.176 you improved name mangling, but I've found one very confusing change...is it your intension to reuse the "b" which used to mean "bit," to now mean a "bool" instead of "x?"For example, one function "real vdb(real, real, real, real, real, real, bool = false)" used in my financial D dll, before dmd v0.148 was released when the "bool" data type was added (replacing the "bit" data type) the function's mangled name was "D13financial_dll3vdbFeeeeeebZe". And with dmd v0.148 and later, the "bit" data type become an alias to bool and all the "b" (for "bit") were replaced with "x" (for "bool"), thus the funtion's mangled name changed to become "D13financial_dll3vdbFeeeeeexZe". But now it looks like "x" has been replaced by "b," is this a mistake on your part, or have you decided to use a "b" as meaning a "bool" (discarding "x")?Between 0.148 and 0.175, function pointers used 'b' for 'bool', but the functions themselves used 'x'. It was a bit inconsistent.
Dec 10 2006
Don thanks for clarifying the reason, I didn't know about the function pointers still using 'b' after bool replaced bit. It all now makes a lot more sense as to the why change happened the way it did. David L. ------------------------------------------------------------------- "Dare to reach for the Stars...Dare to Dream, Build, and Achieve!" ------------------------------------------------------------------- MKoD: http://spottedtiger.tripod.com/D_Language/D_Main_XP.html "Don Clugston" <dac nospam.com.au> wrote in message news:elho1o$t1d$1 digitaldaemon.com...David L. Davis wrote:Walter in dmd v0.176 you improved name mangling, but I've found one very confusing change...is it your intension to reuse the "b" which used to mean "bit," to now mean a "bool" instead of "x?"For example, one function "real vdb(real, real, real, real, real, real, bool = false)" used in my financial D dll, before dmd v0.148 was released when the "bool" data type was added (replacing the "bit" data type) the function's mangled name was "D13financial_dll3vdbFeeeeeebZe". And with dmd v0.148 and later, the "bit" data type become an alias to bool and all the "b" (for "bit") were replaced with "x" (for "bool"), thus the funtion's mangled name changed to become "D13financial_dll3vdbFeeeeeexZe". But now it looks like "x" has been replaced by "b," is this a mistake on your part, or have you decided to use a "b" as meaning a "bool" (discarding "x")?Between 0.148 and 0.175, function pointers used 'b' for 'bool', but the functions themselves used 'x'. It was a bit inconsistent.
Dec 12 2006
"Walter Bright" <newshound digitalmars.com> wrote in message news:ele2k9$2hr5$1 digitaldaemon.com...More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zipNo offense, but would it honestly kill you to allow ctors in structs? We've been using static opCall as a _workaround_ for the lack of struct ctors, and making it part of the language doesn't really seem to be addressing the problem. That, and it doesn't make any sense that classes use "this()" and structs use "static S opCall()". We're starting to get into "overloading indexing in C++" territory. That, and which would be more efficient? The static opCall needs to return an instance of the struct on the stack, which could be very inefficient for large structs. With a true ctor, the struct is passed by reference to the ctor and has its fields set directly. And I also can't figure out how to make the implicit opCall work with more than one parameter. :P I'm also a little wary about opAssign. Not that I don't find it useful, just that you've been vehemently opposed to overloading it in D for how many years, and three weeks before 1.0 you reverse your opinion. What does that mean? But complaints aside, thanks for removing the []=>* implicit cast and the bugfixes. Man, this last month has been an exciting time for D :)
Dec 09 2006
Jarrett Billingsley wrote:"Walter Bright" <newshound digitalmars.com> wrote in message news:ele2k9$2hr5$1 digitaldaemon.com...The thing with opAssign, I realised after reading the spec, is that it explicitly doesn't allow copy assignment. This reduces it from the level of a powerful feature to a mere toy, or at most a form of implicit casting. Assigning one struct to another still guarantees a bitwise copy, and reference assignment for class instances cannot be stepped on. This removes essentially all of the danger that opAssign might have introduced.More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zipNo offense, but would it honestly kill you to allow ctors in structs? We've been using static opCall as a _workaround_ for the lack of struct ctors, and making it part of the language doesn't really seem to be addressing the problem. That, and it doesn't make any sense that classes use "this()" and structs use "static S opCall()". We're starting to get into "overloading indexing in C++" territory. That, and which would be more efficient? The static opCall needs to return an instance of the struct on the stack, which could be very inefficient for large structs. With a true ctor, the struct is passed by reference to the ctor and has its fields set directly. And I also can't figure out how to make the implicit opCall work with more than one parameter. :P I'm also a little wary about opAssign. Not that I don't find it useful, just that you've been vehemently opposed to overloading it in D for how many years, and three weeks before 1.0 you reverse your opinion. What does that mean?But complaints aside, thanks for removing the []=>* implicit cast and the bugfixes. Man, this last month has been an exciting time for D :)-- Kirk McDonald Pyd: Wrapping Python with D http://pyd.dsource.org
Dec 09 2006
Jarrett Billingsley wrote:No offense, but would it honestly kill you to allow ctors in structs? We've been using static opCall as a _workaround_ for the lack of struct ctors, and making it part of the language doesn't really seem to be addressing the problem. That, and it doesn't make any sense that classes use "this()" and structs use "static S opCall()". We're starting to get into "overloading indexing in C++" territory.It turns out that static opCall() and this() are equivalent. C++ has some obscure rules to try to disambiguate them. I wished to avoid that problem, and since static opCall() is routinely in use, picked them.That, and which would be more efficient? The static opCall needs to return an instance of the struct on the stack, which could be very inefficient for large structs. With a true ctor, the struct is passed by reference to the ctor and has its fields set directly.The optimizer removes the redundant copy, and builds the result directly into the target.And I also can't figure out how to make the implicit opCall work with more than one parameter. :PS(1,2,3)I'm also a little wary about opAssign. Not that I don't find it useful, just that you've been vehemently opposed to overloading it in D for how many years, and three weeks before 1.0 you reverse your opinion. What does that mean?The problem I have, and still have, is the identity assignment. This is specifically disallowed with opAssign. I can go into the reasons why if you like. Having opAssign for foreign types turns out to be needed for things like, say I want to build a ranged integer. This is an integer that can only have values between m and n. Without opAssign, I'd have to use a property: RangedInt!(m,n) r; r.value = 6; It's just not right.But complaints aside, thanks for removing the []=>* implicit cast and the bugfixes. Man, this last month has been an exciting time for D :)For me as well!
Dec 09 2006
"Walter Bright" <newshound digitalmars.com> wrote in message news:elf9mn$snp$1 digitaldaemon.com...It turns out that static opCall() and this() are equivalent. C++ has some obscure rules to try to disambiguate them. I wished to avoid that problem, and since static opCall() is routinely in use, picked them.For classes they're not. Why should structs be any different? And even if static opCalls are routinely in use, are they the best solution? And are they _really_ equivalent? Where's the 'this' pointer in a static opCall()?The optimizer removes the redundant copy, and builds the result directly into the target.Wouldn't it be easier to just have ctors, so implementations don't have to worry about writing an optimizer just to support this behavior?Oh. I was thinking more along the lines of being able to do "S s(1, 2, 3);".And I also can't figure out how to make the implicit opCall work with more than one parameter. :PS(1,2,3)The problem I have, and still have, is the identity assignment. This is specifically disallowed with opAssign. I can go into the reasons why if you like. Having opAssign for foreign types turns out to be needed for things like, say I want to build a ranged integer. This is an integer that can only have values between m and n. Without opAssign, I'd have to use a property: RangedInt!(m,n) r; r.value = 6; It's just not right.That makes sense. It gives the utility of opAssign in most cases without the scary stuff. Though as was mentioned elsewhere, it's not commutative, so it's still not possible to do RangedInt!(m, n) r; r = 5; int x = r; // error :/But complaints aside, thanks for removing the []=>* implicit cast and the bugfixes. Man, this last month has been an exciting time for D :)For me as well!
Dec 09 2006
Jarrett Billingsley wrote:I've found they work well.It turns out that static opCall() and this() are equivalent. C++ has some obscure rules to try to disambiguate them. I wished to avoid that problem, and since static opCall() is routinely in use, picked them.For classes they're not. Why should structs be any different? And even if static opCalls are routinely in use, are they the best solution?And are they _really_ equivalent?Not perfectly, but the differences are small.Where's the 'this' pointer in a static opCall()?It's in your return value.You have to have the optimization anyway. My C++ compiler has had it since 1991 or so. It's oooold technology.The optimizer removes the redundant copy, and builds the result directly into the target.Wouldn't it be easier to just have ctors, so implementations don't have to worry about writing an optimizer just to support this behavior?I prefer: auto s = S(1,2,3); to C++ style. static opCall fits right in.Oh. I was thinking more along the lines of being able to do "S s(1, 2, 3);".And I also can't figure out how to make the implicit opCall work with more than one parameter. :PS(1,2,3)Though as was mentioned elsewhere, it's not commutative, so it's still not possible to do RangedInt!(m, n) r; r = 5; int x = r; // errorThat's implicit casting from a struct. It'll probably wind up getting supported in one form or another.
Dec 09 2006
Walter Bright wrote:Jarrett Billingsley wrote:Do you have a single instance of a static opCall being used for construction where you don't just create a variable on the stack, build it, and return it? Because that's all I've ever done. Hundreds of times. Nothing else, and it just ends up being useless fluff making code harder to read than it should be. The opposite end of this would be so easy - if classes didn't have constructors. They don't actually need them after all - you just need to have the ability to create a blank instance. But can you see how much of a pointless pain in the ass that would be? That's exactly how it is in structs right now.I've found they work well.It turns out that static opCall() and this() are equivalent. C++ has some obscure rules to try to disambiguate them. I wished to avoid that problem, and since static opCall() is routinely in use, picked them.For classes they're not. Why should structs be any different? And even if static opCalls are routinely in use, are they the best solution?Yeah, I've never liked C++'s handling of this, it looks way too much like a function call.I prefer: auto s = S(1,2,3); to C++ style. static opCall fits right in.Oh. I was thinking more along the lines of being able to do "S s(1, 2, 3);".And I also can't figure out how to make the implicit opCall work with more than one parameter. :PS(1,2,3)
Dec 09 2006
Burton Radons wrote:Do you have a single instance of a static opCall being used for construction where you don't just create a variable on the stack, build it, and return it? Because that's all I've ever done. Hundreds of times. Nothing else, and it just ends up being useless fluff making code harder to read than it should be.Given: auto s = S(1,2,3); I'm not sure what you mean?
Dec 09 2006
"Walter Bright" <newshound digitalmars.com> wrote in message news:elfg41$139h$1 digitaldaemon.com...I've found they work well.Okay, this obviously isn't working. Let's go at this from another angle. What is the TRUE reason you don't want to give structs ctors? I don't want to know why static opCalls are good, but why ctors are bad.And are they _really_ equivalent?Not perfectly, but the differences are small.Where's the 'this' pointer in a static opCall()?It's in your return value.
Dec 09 2006
Jarrett Billingsley wrote:What is the TRUE reason you don't want to give structs ctors? I don't want to know why static opCalls are good, but why ctors are bad.Constructor: S(v); static opCall: S(v) What's the difference? I just don't see the point for adding constructors.
Dec 09 2006
Walter Bright wrote:Jarrett Billingsley wrote:Here's the difference: struct S { this (int x) { w = calculate_something (x); } this (int x, int y) { this (x); z = calculate (y); } } struct S { static S opCall (int x) { S result; result.build (x); return result; } static S opCall (int x, int y) { S result; result.build (x, y); return result; } private void build (int x) { w = calculate (x); } private void build (int x, int y) { build (x); z = calculate (y); } } Look at all that waste! More than 50% of the code there is just restating things. This was much harder to write, is harder to maintain, and has granted us no benefits whatsoever.What is the TRUE reason you don't want to give structs ctors? I don't want to know why static opCalls are good, but why ctors are bad.Constructor: S(v); static opCall: S(v) What's the difference? I just don't see the point for adding constructors.
Dec 09 2006
Burton Radons wrote:Here's the difference: struct S { this (int x) { w = calculate_something (x); } this (int x, int y) { this (x); z = calculate (y); } }struct S { static S opCall(int x) { S result; result.w = calculate_something(x); return result; } static S opCall(int x, int y) { auto result = S(x); result.z = calculate(y); return result; } } It's 3 more lines of code. The two styles almost completely overlap, and since the latter is already in use, adding ctors just seems redundant.
Dec 09 2006
Walter Bright wrote:Burton Radons wrote:Struct can have opCall, which is why they were used as a workaround for ctors. This was almost ok, but creating language support (through statement rewriting) for a workaround sounds a very bad thing to add prior to 1.0. This is certainly controversial enough, to think over for more than 2 weeks. I personally agree with those who want struct ctors. -- Lars Ivar Igesund blog at http://larsivi.net DSource & #D: larsiviHere's the difference: struct S { this (int x) { w = calculate_something (x); } this (int x, int y) { this (x); z = calculate (y); } }struct S { static S opCall(int x) { S result; result.w = calculate_something(x); return result; } static S opCall(int x, int y) { auto result = S(x); result.z = calculate(y); return result; } } It's 3 more lines of code. The two styles almost completely overlap, and since the latter is already in use, adding ctors just seems redundant.
Dec 10 2006
Walter Bright wrote:Burton Radons wrote:OK here are the things that make me want constructors instead of static opCall: It takes a lot longer to type. You say only 3 more lines, but in that example 9 lines of constructor code becomes 12 lines of opCall code. The resulting code that is specific to these features is 25% fluff, and that includes the trivial curly braces. Character wise it is worse. The typing doesn't bug me as much as this though: what if the struct's name changes? And what if the opCall is heavily overloaded when the name changes? It's more unneeded code refactoring. That is one reason D constructors are so cool, and it kinda sucks that structs don't have that too. Also, static opCall is almost always used in the same way as a constructor. I'd expect them to have the same syntax, but they don't. I think this, and some of the above reaons, result in the workaround perception - everyone expects the smooth 'this' syntax, but get static opCall instead. Now I read that you would like to keep the semantic differences intact because they are useful. Forcing the possibility of a bitwise copy of the struct before it is unleashed will apparently allow for cool stuff. I don't think it's too unreasonable to have the 'this' identifier be a value rather than a reference in a struct constructor. Thus you have a function that implicitly creates a blank instance of the struct, then allows the programmer to modify it via 'this', and implicitly returns the 'this' struct as static opCall would.Here's the difference: struct S { this (int x) { w = calculate_something (x); } this (int x, int y) { this (x); z = calculate (y); } }struct S { static S opCall(int x) { S result; result.w = calculate_something(x); return result; } static S opCall(int x, int y) { auto result = S(x); result.z = calculate(y); return result; } } It's 3 more lines of code. The two styles almost completely overlap, and since the latter is already in use, adding ctors just seems redundant.
Dec 11 2006
"Walter Bright" <newshound digitalmars.com> wrote in message news:elfreq$1dvs$1 digitaldaemon.com...Constructor: S(v); static opCall: S(v) What's the difference? I just don't see the point for adding constructors.In addition to what Burton posted, class: class C { this() { } } Struct: struct S { static S opCall() { S s; return s; } } It's completely un-orthogonal. Wouldn't it be so much cleaner and make so much more sense to allow ctors for structs? Isn't this the _purpose_ of ctors, to initialize members to useful values? Why have static opCall mean something completely different for structs and classes?
Dec 09 2006
Walter Bright wrote:Jarrett Billingsley wrote:The difference is that a ctor initializes an already-existing instance, while static opCall returns one, which is then copied. A static opCall won't handle the following case: ---- void delegate() globalDG; struct Foo { void func() { printf("myVal = %d\n", myVal); } static Foo opCall(int v) { Foo res; res.myVal = v; globalDG = &res.func; return res; } int myVal; } void main() { Foo f = Foo(5); globalDG(); // prints garbage instead of '5' } ---- The Foo instance returned from static opCall is copied, thus the 'ctor hack' doesn't have real access to the object it's constructing, not to mention the overhead of copying the struct to another place on stack... Also, new Foo(5) isn't going to work without real ctors or more hacks. -- Tomasz StachowiakWhat is the TRUE reason you don't want to give structs ctors? I don't want to know why static opCalls are good, but why ctors are bad.Constructor: S(v); static opCall: S(v) What's the difference? I just don't see the point for adding constructors.
Dec 09 2006
Tom S wrote:A static opCall won't handle the following case: ---- void delegate() globalDG; struct Foo { void func() { printf("myVal = %d\n", myVal); } static Foo opCall(int v) { Foo res; res.myVal = v; globalDG = &res.func; return res; } int myVal; } void main() { Foo f = Foo(5); globalDG(); // prints garbage instead of '5' } ----Ok, you're right on that one. But I am going to argue on philosophical grounds that this kind of code is incorrect for a couple of reasons: 1) constructors should construct objects, not set state outside themselves. 2) trying to keep track of every struct created is going to put a hole below the waterline in some new capabilities planned for the future. The compiler needs to be able to willy-nilly create bit copies of structs for this to work. C++ has this problem in spades (just follow the skin-rending and hair-pulling going on with the "move constructor" issues). Allowing the compiler to be able to create bit copied temporaries without needing to call constructors is going to be worthwhile by enabling some pretty cool stuff. I'm going to put forward the idea that this kind of thing should be done with classes, not structs.The Foo instance returned from static opCall is copied, thus the 'ctor hack' doesn't have real access to the object it's constructing, not to mention the overhead of copying the struct to another place on stack...It's time to put the recurring efficiency argument to bed. Consider this D code: ------------------- struct S { static S opCall(int v) { S result; result.v = v; return result; } int v; } int test() { auto s = S(5); return s.v; } ------------compiles to---------- _D4test1S6opCallFiZS4test1S comdat ret _D4test4testFZi comdat mov EAX,5 ret --------------------------------- It doesn't get any better than that.Also, new Foo(5) isn't going to work without real ctors or more hacks.That can be supported without any more work than supporting it with ctors.
Dec 09 2006
Walter Bright wrote:2) trying to keep track of every struct created is going to put a hole below the waterline in some new capabilities planned for the future. The compiler needs to be able to willy-nilly create bit copies of structs for this to work. C++ has this problem in spades (just follow the skin-rending and hair-pulling going on with the "move constructor" issues). Allowing the compiler to be able to create bit copied temporaries without needing to call constructors is going to be worthwhile by enabling some pretty cool stuff.Intriguing. Evidently the ideas barrel is far from empty. <g>
Dec 10 2006
Don Clugston wrote:Intriguing. Evidently the ideas barrel is far from empty. <g>No chance of that happening <g>.
Dec 13 2006
Walter Bright wrote:It's time to put the recurring efficiency argument to bed. Consider this D code:That is a relief to know. What about heap construction? I think currently with the opCall approach you have to do something like: auto x = new Struct; *x = Struct(args); Does that copy get optimized away too? There are other issues people are concerned about also. Maybe it does only take two extra lines to define a static opCall vs a constructor, but that extra two lines is basically redundant noise and makes static opCall look hackish. And given that most every struct is going to need a constructor, it is hackish noise that we're all going to have to (and already do) see a lot. Also the name "static opCall" doesn't exactly scream out "i'm a constructor". And there's no guarantee that if I'm given a random struct I'll be able to use StructName() as a constructor. Users are free to do whatever they want with static opCall. With a constructor you have to construct. Just like you said "constructors should construct objects" -- ok, but static opCall is not intrinsically a constructor, any more than a function called make() is a constructor. It might be used as a constructor, or it might not. There's also the consistency issue, that if construction is done with 'this()' for classes it should be done with 'this()' for structs. I can kind of see why you don't want to use this() because in classes that's triggered by using the 'new' keyword, and for stack construct structs it makes sense to not use 'new'. But then if structs can be value constructed with x = Struct(), then why not classes too? But classes have to be value constructed using 'scope'. Ok, what if you just sidestep all that and introduce something like opCreate for structs. Basically this would be identical to the current "static opCall", but you could write the code for it just like a constructor. Like so: opCreate(int arg) { m_member = arg; } I.e. you don't specify return type, return value, or 'static' etc. It works just like a constructor, but you call it like static opCall. auto x = MyStruct() It would be an error to have both a static opCall and an opCreate. Of course at that point it really raises the question "why not just call it 'this' instead of opCreate". And declare that for structs you can invoke 'this' by using x = MyStruct(). Anyway, I really don't see why it has to be an either/or situation. Let people use this/opCreate for new code, and let them continue to use static opCall in old code. But make it an error to have both in a struct. What's so hard about that? --bb
Dec 10 2006
Bill Baxter wrote:There's also the consistency issue, that if construction is done with 'this()' for classes it should be done with 'this()' for structs.One issue I have with allowing struct constructors is that the existence of struct constructors implies the existence of struct destructors. Do we really want to go that far? Though "static opCall" doesn't scream "constructor," it also doesn't seem to imply the user could define a destructor. I submit that "this()" would. -- Kirk McDonald Pyd: Wrapping Python with D http://pyd.dsource.org
Dec 10 2006
Kirk McDonald wrote:Bill Baxter wrote:That seems like a non-issue to me. Even if people do take that to mean you can have a destructor, the compiler will crush any such delusions the moment you try to compile a struct with ~this in it. The compiler error message could even be "Structs cannot have destructors, only classes can". That should be pretty clear. Structs and classes are different in D. That's a fact, and new users are going to have to deal with it sooner or later. Should the language emphasize the similarities or stress the differences? I don't think it makes a big difference in this case. What's more important I think is that core operations can be performed in a way that is clear and unambiguous. No matter how you slice it, construction is a very common operation, and 'static opCall' does not offer a clear syntax for construction behavior, nor is it unambiguous in the sense that it can be used for any purpose the user desires. The fact that everyone was convinced that it must be inefficient because it *looks* like it's going to do extra copies is just another side effect of it using unambiguous constructor-like syntax. Walter went out of his way to avoid "looking hackish" with regard to foreach loops. It seems very odd to just accept hackish-looking constructors for structs. --bbThere's also the consistency issue, that if construction is done with 'this()' for classes it should be done with 'this()' for structs.One issue I have with allowing struct constructors is that the existence of struct constructors implies the existence of struct destructors. Do we really want to go that far? Though "static opCall" doesn't scream "constructor," it also doesn't seem to imply the user could define a destructor. I submit that "this()" would.
Dec 10 2006
Bill Baxter wrote: edit:The fact that everyone was convinced that it must be inefficient because it *looks* like it's going to do extra copies is just another side effect of it using unambiguous constructor-like syntax.I mean "a side effect of it *not* using unambiguous constructor-like syntax", of course. --bb
Dec 10 2006
Kirk McDonald wrote:Bill Baxter wrote:Who knows, maybe D will have destructors for structs one day :o). AndreiThere's also the consistency issue, that if construction is done with 'this()' for classes it should be done with 'this()' for structs.One issue I have with allowing struct constructors is that the existence of struct constructors implies the existence of struct destructors. Do we really want to go that far? Though "static opCall" doesn't scream "constructor," it also doesn't seem to imply the user could define a destructor. I submit that "this()" would.
Dec 10 2006
"Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> wrote in message news:elihab$1q98$1 digitaldaemon.com...Who knows, maybe D will have destructors for structs one day :o). AndreiI can see it now: DMD 0.178 * Added destructors for structs. Big thanks to Andrei.
Dec 10 2006
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message news:eliilm$1sk3$1 digitaldaemon.com...I can see it now: DMD 0.178 * Added destructors for structs. Big thanks to Andrei.You can't see it, but there's a ";)" at the end of that post.
Dec 10 2006
== Quote from Kirk McDonald (kirklin.mcdonald gmail.com)'s articleBill Baxter wrote: One issue I have with allowing struct constructors is that the existence of struct constructors implies the existence of struct destructors. Do we really want to go that far?I certainly do :) Since struct types are always known at compile-time, structs without destructors should not be affected. I think struct destructors will be very useful (RIAA, custom allocation/deallocation strategies).
Dec 11 2006
Walter Bright wrote:Tom S wrote:[...] In many cases, it may be vary good. However consider the case where there is more than one return statement using different variables. You now need a much better optimizer to get this optimized down to "almost nothing". Furthermore, it doesn't reflect what is actually being done. struct S { static S err; int k, l; static S opCall(int i, int j) { S ret; ret.k=i; ret.l=j; if(ret.test) return ret; else ret err; } bool test(){...} } With constructors, it is not only simpler code, but looks like what is happening. struct S { static S err; int k, l; this(int i, int j) { k=i; l=j; if(!ret.test) this = err; } bool test(){...} }The Foo instance returned from static opCall is copied, thus the 'ctor hack' doesn't have real access to the object it's constructing, not to mention the overhead of copying the struct to another place on stack...It's time to put the recurring efficiency argument to bed. Consider this D code:
Dec 11 2006
BCS wrote:With constructors, it is not only simpler code, but looks like what is happening. struct S { static S err; int k, l; this(int i, int j) { k=i; l=j; if(!ret.test) this = err; } bool test(){...} }Assignment to this inside a constructor is a mistake as it breaks the assumptions the language makes about constructors.
Dec 13 2006
Walter Bright wrote:BCS wrote:What assumptions does it break? This would be valid: struct S { static S err; int k, l; this(int i, int j) { k=i; l=j; if(!ret.test) { this.k = err.k; this.l = err.l; } } bool test(){...} } and as far as I can tell, they are the same. OK well maybe it should have been written as this: if(!ret.test) *this = err; // ^- add this Either way, I think the original argument still holds. The constructor form still looks more like what is acutely happening, and as a result has less of a "phantom" cost.With constructors, it is not only simpler code, but looks like what is happening. struct S { static S err; int k, l; this(int i, int j) { k=i; l=j; if(!ret.test) this = err; } bool test(){...} }Assignment to this inside a constructor is a mistake as it breaks the assumptions the language makes about constructors.
Dec 13 2006
BCS wrote:OK well maybe it should have been written as this: if(!ret.test) *this = err; // ^- add thisThat's completely different <g>.Either way, I think the original argument still holds. The constructor form still looks more like what is acutely happening, and as a result has less of a "phantom" cost.
Dec 14 2006
Walter Bright escribió:Jarrett Billingsley wrote:I don't understand: the current way to go (opCall) *is a workaround*. That means, it's frequently used by D programmers to simulate something that is missing in the language (at least in my case). What about elegance (one of the most beloved properties of D)? Doesn't it account at all? Regards, -- Tom;What is the TRUE reason you don't want to give structs ctors? I don't want to know why static opCalls are good, but why ctors are bad.Constructor: S(v); static opCall: S(v) What's the difference? I just don't see the point for adding constructors.
Dec 10 2006
== Quote from Walter Bright (newshound digitalmars.com)'s articleJarrett Billingsley wrote:Yes, but ctors would work even better: this(int a, int b) { _a = a; _b = b; } static Foo opCall(int a, int b) { Foo foo; foo._a = a; foo._b = b; return foo; } Suggestion: disallow both opCall and ctor for the same struct and there should be no ambiguity issues.I've found they work well.It turns out that static opCall() and this() are equivalent. C++ has some obscure rules to try to disambiguate them. I wished to avoid that problem, and since static opCall() is routinely in use, picked them.For classes they're not. Why should structs be any different? And even if static opCalls are routinely in use, are they the best solution?That's implicit casting from a struct. It'll probably wind up getting supported in one form or another.That's great :) It seems that some D issues suffer from "Black hole theory of languages": "C++ is a Black Hole: if you try to design something that's not C++, but like C++, you'll find that the gravitational forces on the design will suck it into the Black Hole, and it will become C++"
Dec 10 2006
Walter Bright wrote:Jarrett Billingsley wrote:Just because someone at some time in the distant pass realized that opCall() simulates constructor-like behavior, it doesn't make it 'ok'; it makes it 'an ok work around'. Think back over the last year.. how many people have asked how to create a constructor for structs? How much you wanna bet none of them would have had to ask if 'this' wasn't misspelled as 'opCall'? Since the desired behavior is construction, let's stop kidding ourselves and actually give structs both the behavior and the syntax of construction, please? And while you're in there.. how about destruction and RAII? I guess I'm in the camp that would like to see structs be closer to classes but am ok with them not supporting inheritance and other features that lead to vtables. Later, BradNo offense, but would it honestly kill you to allow ctors in structs? We've been using static opCall as a _workaround_ for the lack of struct ctors, and making it part of the language doesn't really seem to be addressing the problem. That, and it doesn't make any sense that classes use "this()" and structs use "static S opCall()". We're starting to get into "overloading indexing in C++" territory.It turns out that static opCall() and this() are equivalent. C++ has some obscure rules to try to disambiguate them. I wished to avoid that problem, and since static opCall() is routinely in use, picked them.
Dec 09 2006
Brad Roberts wrote:Since the desired behavior is construction, let's stop kidding ourselves and actually give structs both the behavior and the syntax of construction, please? And while you're in there.. how about destruction and RAII?What ever happened to structs as aggregates? I thought this was their entire purpose for being in D. Adding ctors makes sense because it merely simplifies initialization, but adding dtors, copy semantics, and opAssign all seem bent on making structs into something they're not. After all, isn't that why we have classes? In fact, many of the instances where I was inclined to use structs simply to avoid the GC evaporated when stack construction of classes was added a few releases ago. Aside from the addition of a ctor, I'm quite happy to leave structs exactly as they were before 177. That said, I suppose I can appreciate the desire to give classes opAssign (even though it's somewhat weird), and perhaps the operator was added to structs simply for the sake of consistency. Sean
Dec 11 2006
Sean Kelly wrote:Brad Roberts wrote:Classes are different from structs in two essential ways: 1. Polymorphism 2. Referential semantics The two are actually interdependent, as you can't have polymorphism comfortably unless you have reference semantics. That's the important distinction. Other than that, it's good that they share a number of valuable properties. Take private state for example. structs as sheer unchecked aggregates would provide too little value to be useful. Most of the time, aggregating some state together (date, time, etc.) comes together with some desirable invariant that the aggregate should hold, meaning that not all possible bit patterns of the aggregate can be meaningful. So it's useful that struct has private state. Consequently, they should have member functions (setters, getters) and the such. They also should have constructors so they can put themselves in a meaningful initial state, and should have opAssign so they allow controlled overwriting of their state. (It's an accident that opAssign from the same type has not been implemented yet; it can be safely allowed.) On the other hand, intercepting _duplication_ of structs naively would be a major breach in the object model, because it would frontally collide with (2) above: structs are values and the compiler can move and copy structs around discretionary using bitwise copying. That's why D currently does not allow interception of copy construction and destruction. It might in the future, but in a way that does not clash with the object model. (That is doable.) So my point was, as attractive the simple mantra "structs should be aggregates" is, it turns out it's not that useful. So it's great that D supports efficient and safe user-defined values. AndreiSince the desired behavior is construction, let's stop kidding ourselves and actually give structs both the behavior and the syntax of construction, please? And while you're in there.. how about destruction and RAII?What ever happened to structs as aggregates? I thought this was their entire purpose for being in D. Adding ctors makes sense because it merely simplifies initialization, but adding dtors, copy semantics, and opAssign all seem bent on making structs into something they're not. After all, isn't that why we have classes? In fact, many of the instances where I was inclined to use structs simply to avoid the GC evaporated when stack construction of classes was added a few releases ago. Aside from the addition of a ctor, I'm quite happy to leave structs exactly as they were before 177. That said, I suppose I can appreciate the desire to give classes opAssign (even though it's somewhat weird), and perhaps the operator was added to structs simply for the sake of consistency.
Dec 11 2006
Andrei Alexandrescu (See Website For Email) wrote:Sean Kelly wrote:"allow controlled overwriting of their state" -> Would that be for the case where the struct's private state (aka abstract state) is not just the bit pattern value of the struct, but also the contents of pointer/reference members? -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#DBrad Roberts wrote:Classes are different from structs in two essential ways: 1. Polymorphism 2. Referential semantics The two are actually interdependent, as you can't have polymorphism comfortably unless you have reference semantics. That's the important distinction. Other than that, it's good that they share a number of valuable properties. Take private state for example. structs as sheer unchecked aggregates would provide too little value to be useful. Most of the time, aggregating some state together (date, time, etc.) comes together with some desirable invariant that the aggregate should hold, meaning that not all possible bit patterns of the aggregate can be meaningful. So it's useful that struct has private state. Consequently, they should have member functions (setters, getters) and the such. They also should have constructors so they can put themselves in a meaningful initial state, and should have opAssign so they allow controlled overwriting of their state. (It's an accident that opAssign from the same type has not been implemented yet; it can be safely allowed.)Since the desired behavior is construction, let's stop kidding ourselves and actually give structs both the behavior and the syntax of construction, please? And while you're in there.. how about destruction and RAII?What ever happened to structs as aggregates? I thought this was their entire purpose for being in D. Adding ctors makes sense because it merely simplifies initialization, but adding dtors, copy semantics, and opAssign all seem bent on making structs into something they're not. After all, isn't that why we have classes? In fact, many of the instances where I was inclined to use structs simply to avoid the GC evaporated when stack construction of classes was added a few releases ago. Aside from the addition of a ctor, I'm quite happy to leave structs exactly as they were before 177. That said, I suppose I can appreciate the desire to give classes opAssign (even though it's somewhat weird), and perhaps the operator was added to structs simply for the sake of consistency.
Dec 11 2006
Bruno Medeiros wrote:"allow controlled overwriting of their state" -> Would that be for the case where the struct's private state (aka abstract state) is not just the bit pattern value of the struct, but also the contents of pointer/reference members?That too. The canonical example is simpler - reject invalid attempts at setting state. Consider: struct BoundedInt(int min, int max) { ... } alias BoundedInt!(0, 100) Percent; Percent soFar = 0; ... soFar = bytesCopied * 100 / bytesToCopy; Upon assignment, the bounded int structure should do a runtime check to make sure it is being set to a number within bounds. Andrei
Dec 11 2006
To Andrei Alexandrescu: - Voiam doar sa salut prezenta dvs aici. - I greet your presence here.
Dec 11 2006
Andrei Alexandrescu (See Website For Email) wrote:Classes are different from structs in two essential ways: 1. Polymorphism 2. Referential semantics The two are actually interdependent, as you can't have polymorphism comfortably unless you have reference semantics.Certainly.That's the important distinction. Other than that, it's good that they share a number of valuable properties. Take private state for example. structs as sheer unchecked aggregates would provide too little value to be useful. Most of the time, aggregating some state together (date, time, etc.) comes together with some desirable invariant that the aggregate should hold, meaning that not all possible bit patterns of the aggregate can be meaningful. So it's useful that struct has private state. Consequently, they should have member functions (setters, getters) and the such. They also should have constructors so they can put themselves in a meaningful initial state, and should have opAssign so they allow controlled overwriting of their state. (It's an accident that opAssign from the same type has not been implemented yet; it can be safely allowed.)Point made. And I suppose opAssign isn't truly linked to the idea of object copy semantics--I've just gotten used to associating the two because of my experience with C++.So my point was, as attractive the simple mantra "structs should be aggregates" is, it turns out it's not that useful. So it's great that D supports efficient and safe user-defined values.structs as aggregates do come in quite handy for a few specific situations, but I agree that they aren't tremendously useful in the general sense. It's beginning to seem, then, that common value types in D will be represented as structs, with classes reserved for those types with copy requirements. Once implicit cast support is added, it seems we'll be in pretty good shape for UDTs in D. And here I'd come to accept that we'd never have them :-) Sean
Dec 11 2006
Andrei Alexandrescu (See Website For Email) wrote:Classes are different from structs in two essential ways: 1. Polymorphism 2. Referential semantics The two are actually interdependent, as you can't have polymorphism comfortably unless you have reference semantics.That's one of the things I felt in my bones, but was unable to put my finger on it.
Dec 12 2006
== Quote from Walter Bright (newshound digitalmars.com)'s articleAndrei Alexandrescu (See Website For Email) wrote:Actually, polymorphism does not imply referential semantics (nor does referential semantics imply polymorphism, of course). 1) Value sematics can be achieved with references as well, by using copy-on-write strategy (or by making classes immutable). All that without sacrificing polymorphism. 2) Polymorphism can be achieved without references as well, but then the size of struct could no longer be determined at comile-time. In other words, function 'sizeof' could no longer be parameterless. Or, alternatively, every such 'polymorphic struct' would have to contain two pointers: VMT and additional 'data' pointer (which is actually pretty close to using references anyway). I'm in favour of allowing programmer to mark special semantics, such as value class, or pure function (without side effects). Many additional optimizations can be performed under such assumptions. Most notably: value classes could be destroyed predictably (without causing arbitrary long GC pauses) and calls to pure functions with unchanged parameters can be cached (for example, foo.name().length()...). Also, it would be interesting to mark some functions as compile-time, requiring that they only interact with values that are known in compile-time. This would make metaprogramming much more similar to normal programming. One additional nice thing: you can support such declarations even before optimizer takes advantage of them. Also, I would suggest issuing a compile-time warning when polymorphism is used without explicit 'override' directive. Perhaps coupled with a pragma or something to turn such warning off. PS: I really appreciate all the work that has been put in D. D is going to be a much needed successor of C++. I don't see D as competitor to be a terrible choice), but it sure can rock the non-VM world!Classes are different from structs in two essential ways: 1. Polymorphism 2. Referential semantics The two are actually interdependent, as you can't have polymorphism comfortably unless you have reference semantics.That's one of the things I felt in my bones, but was unable to put my finger on it.
Dec 12 2006
Boris Kolar wrote:PS: I really appreciate all the work that has been put in D. D is going to be a much needed successor of C++. I don't see D as competitor to be a terrible choice), but it sure can rock the non-VM world!Why not? I think that D could rock the .NET world too. I always wished (and still do) for a d.net implementation.
Dec 12 2006
Ivan Senji wrote:Boris Kolar wrote:.NET is not the solution for all problems. There will be other library approaches that maybe even perform better and use features of D that make it like .NET on steroids. I bet after some time of an official, fixed D 1.0 specification, there will some libraries pop up that may serve needs like .NET or J2EE and what not do, also. Apart from that.. hey, you're posting this in the official D newsgroup ;) . Kind regards, AlexPS: I really appreciate all the work that has been put in D. D is going to be a much needed successor of C++. I don't see D as competitor to be a terrible choice), but it sure can rock the non-VM world!Why not? I think that D could rock the .NET world too. I always wished (and still do) for a d.net implementation.
Dec 12 2006
Alexander Panek wrote:Ivan Senji wrote:Naturally. That is why we are here (using D).Boris Kolar wrote:..NET is not the solution for all problems.PS: I really appreciate all the work that has been put in D. D is going to be a much needed successor of C++. I don't see D as competitor to be a terrible choice), but it sure can rock the non-VM world!Why not? I think that D could rock the .NET world too. I always wished (and still do) for a d.net implementation.There will be other library approaches that maybe even perform better and use features of D that make it like .NET on steroids. I bet after some time of an official, fixed D 1.0 specification, there will some libraries pop up that may serve needs like .NET or J2EE and what not do, also.I sure hope so. What I was trying to say programming in .NET is sometimes required andApart from that.. hey, you're posting this in the official D newsgroup ;) .Is that a problem? :)
Dec 12 2006
Ivan Senji wrote:Alexander Panek wrote:Exacteley!Ivan Senji wrote:Naturally. That is why we are here (using D).Boris Kolar wrote:..NET is not the solution for all problems.PS: I really appreciate all the work that has been put in D. D is going to be a much needed successor of C++. I don't see D as competitor to be a terrible choice), but it sure can rock the non-VM world!Why not? I think that D could rock the .NET world too. I always wished (and still do) for a d.net implementation.Are you, actually, talking about a D -> MSIL compiler, so it can be run inside the VM and a .NET implementation in D, for that purpose? Or do like that has to be in D. :) I hope this will pop upThere will be other library approaches that maybe even perform better and use features of D that make it like .NET on steroids. I bet after some time of an official, fixed D 1.0 specification, there will some libraries pop up that may serve needs like .NET or J2EE and what not do, also.I sure hope so. What I was trying to say programming in .NET is sometimes required andOf course not. Was just a side-kick regarding your "I want .NET in D" statement :) . Kind regards, AlexApart from that.. hey, you're posting this in the official D newsgroup ;) .Is that a problem? :)
Dec 12 2006
Alexander Panek wrote:Ivan Senji wrote:Actually I really was talking about a D->MSIL compiler for those cases when you just have to use .net and would still prefer other benefits of D. But the other thing would be great too.I sure hope so. What I was trying to say programming in .NET is sometimes required andAre you, actually, talking about a D -> MSIL compiler, so it can be run inside the VM and a .NET implementation in D, for that purpose? Or do like that has to be in D. :) I hope this will pop up:POf course not. Was just a side-kick regarding your "I want .NET in D" statement :) .Apart from that.. hey, you're posting this in the official D newsgroup ;) .Is that a problem? :)
Dec 12 2006
"Alexander Panek" <a.panek brainsware.org> wrote in message news:eln5td$2pta$1 digitaldaemon.com...Are you, actually, talking about a D -> MSIL compiler, so it can be run inside the VM and a .NET implementation in D, for that purpose? Or do you has to be in D. :) I hope this will pop upWe had one, once : ( but then the guy's HDD crashed... Sad, sad story. L.
Dec 13 2006
Lionello Lunesu wrote:"Alexander Panek" <a.panek brainsware.org> wrote in message news:eln5td$2pta$1 digitaldaemon.com...https://mywebspace.wisc.edu/daaugustine/web/d/ This is all that remains of all his work, so far as I can tell. Some day it would be nice if http://www.scratch-ware.net/dfiles/ suddenly existed again. Oh well. -- Chris Nicholson-SaulsAre you, actually, talking about a D -> MSIL compiler, so it can be run inside the VM and a .NET implementation in D, for that purpose? Or do you has to be in D. :) I hope this will pop upWe had one, once : ( but then the guy's HDD crashed... Sad, sad story. L.
Dec 14 2006
Chris Nicholson-Sauls wrote:Lionello Lunesu wrote:I think I have pretty much all of the binary files he released (though they're archived on some CD in my closet), but he never released any source before his hard drive crashed, so I don't have any of that. And I think the binaries targeted some beta version of .NET and required .dll's that you'd only have if you installed a beta version of Visual Studio. Seemed like a cool idea, but it took a lot of effort just to try it out... -- jcc7"Alexander Panek" <a.panek brainsware.org> wrote in message news:eln5td$2pta$1 digitaldaemon.com...https://mywebspace.wisc.edu/daaugustine/web/d/ This is all that remains of all his work, so far as I can tell. Some day it would be nice if http://www.scratch-ware.net/dfiles/ suddenly existed again. Oh well. -- Chris Nicholson-SaulsAre you, actually, talking about a D -> MSIL compiler, so it can be run inside the VM and a .NET implementation in D, for that purpose? Something like that has to be in D. :) I hope this will pop upWe had one, once : ( but then the guy's HDD crashed... Sad, sad story. L.
Dec 14 2006
Justin C Calvarese wrote:Chris Nicholson-Sauls wrote:Can't assemblies be fairly easily disassembled? L.Lionello Lunesu wrote:I think I have pretty much all of the binary files he released (though they're archived on some CD in my closet), but he never released any source before his hard drive crashed, so I don't have any of that. And I think the binaries targeted some beta version of .NET and required .dll's that you'd only have if you installed a beta version of Visual Studio."Alexander Panek" <a.panek brainsware.org> wrote in message news:eln5td$2pta$1 digitaldaemon.com...https://mywebspace.wisc.edu/daaugustine/web/d/ This is all that remains of all his work, so far as I can tell. Some day it would be nice if http://www.scratch-ware.net/dfiles/ suddenly existed again. Oh well. -- Chris Nicholson-SaulsAre you, actually, talking about a D -> MSIL compiler, so it can be run inside the VM and a .NET implementation in D, for that purpose? Something like that has to be in D. :) I hope this will pop upWe had one, once : ( but then the guy's HDD crashed... Sad, sad story. L.
Dec 15 2006
Lionello Lunesu wrote:Justin C Calvarese wrote:Yes. In fact, the times I've done it I've seen an exact copy of the original source code (!). Try Reflector from this link: http://www.aisto.com/roeder/dotnet/ SeanChris Nicholson-Sauls wrote:Can't assemblies be fairly easily disassembled?Lionello Lunesu wrote:I think I have pretty much all of the binary files he released (though they're archived on some CD in my closet), but he never released any source before his hard drive crashed, so I don't have any of that. And I think the binaries targeted some beta version of .NET and required .dll's that you'd only have if you installed a beta version of Visual Studio."Alexander Panek" <a.panek brainsware.org> wrote in message news:eln5td$2pta$1 digitaldaemon.com...https://mywebspace.wisc.edu/daaugustine/web/d/ This is all that remains of all his work, so far as I can tell. Some day it would be nice if http://www.scratch-ware.net/dfiles/ suddenly existed again. Oh well. -- Chris Nicholson-SaulsAre you, actually, talking about a D -> MSIL compiler, so it can be run inside the VM and a .NET implementation in D, for that purpose? Something like that has to be in D. :) I hope this will pop upWe had one, once : ( but then the guy's HDD crashed... Sad, sad story. L.
Dec 15 2006
Boris Kolar wrote:== Quote from Walter Bright (newshound digitalmars.com)'s articleOf course. It's a "true but uninteresting" fact. Even in current D you can think that int is a reference and that ++i rebinds i to another value. But when talking about reference semantics, that means object is unique, references are many. Doing copy-on-write takes reference-ness out of references, so of course then they start behaving like value.Andrei Alexandrescu (See Website For Email) wrote:Actually, polymorphism does not imply referential semantics (nor does referential semantics imply polymorphism, of course). 1) Value sematics can be achieved with references as well, by using copy-on-write strategy (or by making classes immutable). All that without sacrificing polymorphism.Classes are different from structs in two essential ways: 1. Polymorphism 2. Referential semantics The two are actually interdependent, as you can't have polymorphism comfortably unless you have reference semantics.That's one of the things I felt in my bones, but was unable to put my finger on it.2) Polymorphism can be achieved without references as well, but then the size of struct could no longer be determined at comile-time. In other words, function 'sizeof' could no longer be parameterless. Or, alternatively, every such 'polymorphic struct' would have to contain two pointers: VMT and additional 'data' pointer (which is actually pretty close to using references anyway).Yup, that's reference all right. Some Java implementations do this. Andrei
Dec 12 2006
Walter Bright wrote:Andrei Alexandrescu (See Website For Email) wrote:I feel it in my bones too. It hurts when it rains :o). AndreiClasses are different from structs in two essential ways: 1. Polymorphism 2. Referential semantics The two are actually interdependent, as you can't have polymorphism comfortably unless you have reference semantics.That's one of the things I felt in my bones, but was unable to put my finger on it.
Dec 12 2006
On Mon, 11 Dec 2006 10:50:41 -0800, Sean Kelly <sean f4.ca> wrote:Brad Roberts wrote:Since the desired behavior is construction, let's stop kidding =of =ourselves and actually give structs both the behavior and the syntax =construction, please? And while you're in there.. how about ==destruction and RAII?What ever happened to structs as aggregates? I thought this was their=entire purpose for being in D. Adding ctors makes sense because it =merely simplifies initialization, but adding dtors, copy semantics, an=d =opAssign all seem bent on making structs into something they're not. =After all, isn't that why we have classes? In fact, many of the =instances where I was inclined to use structs simply to avoid the GC =evaporated when stack construction of classes was added a few releases==ago. Aside from the addition of a ctor, I'm quite happy to leave =structs exactly as they were before 177. That said, I suppose I can =appreciate the desire to give classes opAssign (even though it's =somewhat weird), and perhaps the operator was added to structs simply ==for the sake of consistency. SeanI used to be convinced that struct's needed constructors; but now I see = = that there is a steam-roll effect as the demand for one class-like = attribute is proposed: suddenly people want more class-related = functionality or people argue that there is a basis for additional = features for the sake of consistancy. Then the redesign of structs star= ts = taking on the appearance of a reegineered, specialized class -- somethin= g = of a "context switch" too since classes use reference semantics. Perhap= s = we should be careful about hurtling too far in the direction of C++? From the perspective of setting initial state, classes in the OOP conte= xt = use constructors to do that. Structs were never intended to have OOP li= ke = syntax (or so it seems to me). Why can't structs just use an initialize= r = like constants or statics? I notice that Walter has added the struct = initialization using S(x) syntax. I think it is somewhat strange, but I= = suppose that was designed to be an alternative to constructor = initialization? What happened to something like a struct literal = intializer (which only works for static structs and constant values)? W= hy = can't such initialization be extended to local structs? ... struct S { int i; bool b; } static S t =3D { 5, true }; // must be "static" or initialization won't= work ... The above only works for static Structs. But that kind of initializatio= n = seems to be more consistant with the imperitive style of structs verses = = the OOP style of classes (if only non-statics could be initialized too, = = that is). When all is said and done, this is just like doing the same as= = S( 5, 2 ) since 0.177. But I guess as soon as we start adding "methods" to the struct that are = = responsible for changing state in the struct, a whole new set of = principles comes to work. Adding constructor funtionality via "this()" = is = only one way to fix it. opCall, the current way, is a rather ugly = default. If it is important to keep structs distinct from classes, then= = structs need to adopt a alternate way of doing initialization (but pleas= e = not opCall). I don't think "this()" really is the optimal way, though = perhaps the most familiar due to the influence of classes. I think ther= e = should remain a strong distinction between class and struct. If, nevertheless, Walter decides to implement a "this" constructor for = struct, I really hope he doesn't feel the need to go with a destructor a= nd = more class-like functionality as well. Ironically, I feel more sympathetic to what appears to be Walter's opini= on = on the matter this time around. :D -JJR
Dec 11 2006
John Reimer wrote:From the perspective of setting initial state, classes in the OOP context use constructors to do that. Structs were never intended to have OOP like syntax (or so it seems to me). Why can't structs just use an initializer like constants or statics? I notice that Walter has added the struct initialization using S(x) syntax. I think it is somewhat strange, but I suppose that was designed to be an alternative to constructor initialization? What happened to something like a struct literal intializer (which only works for static structs and constant values)? Why can't such initialization be extended to local structs? .... struct S { int i; bool b; } static S t = { 5, true }; // must be "static" or initialization won't workI think the problem here is that such initializers would not apply to private data, and more to the point, a ctor can do more than simply assign a constant to each member of the struct. But it would be nice if this syntax worked for non-static structs anyway.But I guess as soon as we start adding "methods" to the struct that are responsible for changing state in the struct, a whole new set of principles comes to work. Adding constructor funtionality via "this()" is only one way to fix it. opCall, the current way, is a rather ugly default. If it is important to keep structs distinct from classes, then structs need to adopt a alternate way of doing initialization (but please not opCall). I don't think "this()" really is the optimal way, though perhaps the most familiar due to the influence of classes. I think there should remain a strong distinction between class and struct.As do I. And I suppose the presence or lack of polymorphism is a sufficient distinction--the underpinnings for polymorphism do have a fairly significant impact on how an object is represented internally, etc.If, nevertheless, Walter decides to implement a "this" constructor for struct, I really hope he doesn't feel the need to go with a destructor and more class-like functionality as well.The idea of a dtor for structs doesn't make much sense to me because of the bit-copy semantics. When would the dtor for a struct used as a return value be called? Sean
Dec 11 2006
On Mon, 11 Dec 2006 15:57:51 -0800, Sean Kelly <sean f4.ca> wrote:John Reimer wrote:From the perspective of setting initial state, classes in the OOP ==context use constructors to do that. Structs were never intended to ==have OOP like syntax (or so it seems to me). Why can't structs just =as =use an initializer like constants or statics? I notice that Walter h=added the struct initialization using S(x) syntax. I think it is =e =somewhat strange, but I suppose that was designed to be an alternativ=to constructor initialization? What happened to something like a =struct literal intializer (which only works for static structs and ==constant values)? Why can't such initialization be extended to local=on't =structs? .... struct S { int i; bool b; } static S t =3D { 5, true }; // must be "static" or initialization w==workI think the problem here is that such initializers would not apply to =private data, and more to the point, a ctor can do more than simply =assign a constant to each member of the struct. But it would be nice =if =this syntax worked for non-static structs anyway.Ah true. But then, maybe there shouldn't be private data in a struct = either. :) I still see structs as a fairly basic non-OOP entity. -JJR
Dec 11 2006
Walter Bright wrote:I wonder if it is not possible to make D, working as a base with values not with references. In such a case references would be hidden from programmers and only values would be visible at first. Assigning would always mean assign value, and when there should be passed reference the special syntax would be necessary, so references would be special case. To disambiguate you can make something like this: auto v := new Any; // special assignment operator ':=' for references auto z := new Any; v = 5; // Standard opAssign z = v; // No problem here, opAssign can be used as usually z :=v; // Reference copy - opAssign is not used here Additionally when passing arguments to function, which demands references, there could be used implicit conversion from values to references (so that for user it would be visible as sending values). Below example almost works now (almost because constructors can not be templatized): void varFunc(Any[] arr ...) { Â Â Â Â foreach(v; arr) writefln(v.type); } void main() { } In language in which everything is consider as an object you could just write: void main() { bool test; } If there should be send reference(pointer) it should be done explicitly. E.g. void varFunc(Any*[] arr ...) { .... } void main() { } Does it make any sense? Please comment...I'm also a little wary about opAssign. Â Not that I don't find it useful, just that you've been vehemently opposed to overloading it in D for how many years, and three weeks before 1.0 you reverse your opinion. Â What does that mean?The problem I have, and still have, is the identity assignment. This is specifically disallowed with opAssign. I can go into the reasons why if you like.Having opAssign for foreign types turns out to be needed for things like, say I want to build a ranged integer. This is an integer that can only have values between m and n. Without opAssign, I'd have to use a property: RangedInt!(m,n) r; r.value = 6; It's just not right.the same in case of boost::Any port to D. -- Regards Marcin Kuszczak (Aarti_pl)
Dec 09 2006
Walter Bright wrote:More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zipopAssing not mentioned here, but anyway I like what I have read in Docs! Finally I can use more user friendly and compact syntax for assigning values to Any (http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D.announce&artnum=4827): auto v=new Any; Was: v.assign(5); Now: v=5; Very good syntax for this kind of container! I just miss templatized constructors and following will be available: void varFunc(Any[] arr ...) { foreach(v; arr) { writefln(v.type); } } void main() { bool test; } (Currently it is necessary to write: ) I will send updated version of Any soon... -- Regards Marcin Kuszczak (Aarti_pl)
Dec 09 2006
Walter Bright wrote:More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zipCan you clarify more what that means? enable more thorough rtti. So, can we get a string version of enum values? and can we get struct names? Why is everyone talking about opAssign? I don't see it mentioned in the change log.
Dec 09 2006
"Hasan Aljudy" <hasan.aljudy gmail.com> wrote in message news:elfcgr$v45$1 digitaldaemon.com...Why is everyone talking about opAssign? I don't see it mentioned in the change log.Have a look on the "Operator Overloading" page of the spec.
Dec 09 2006
Walter Bright wrote:More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zipI'm not sure if this is a bug or what, but to get derelict from not spitting out this error message... derelict\opengl\glu.obj(glu) Error 42: Symbol Undefined _D8derelict6opengl3glu8GLUnurbs6__initZ derelict\opengl\glu.obj(glu) Error 42: Symbol Undefined _D8derelict6opengl3glu10GLUquadric6__initZ derelict\opengl\glu.obj(glu) Error 42: Symbol Undefined _D8derelict6opengl3glu13GLUtesselator6__initZ In glu.d line 287-289 I had to change struct GLUnurbs; struct GLUquadric; struct GLUtesselator; to struct GLUnurbs{} struct GLUquadric{} struct GLUtesselator{} In order for the compiler to recognize the symbols. Thanks for the release. ~ Clay
Dec 09 2006
did you recompile linked D libs?
Dec 10 2006
Frank Benoit (keinfarbton) wrote:did you recompile linked D libs?Yes, I actually don't use any linked libs since D compiles so fast. I just let build pull in what it wants to.
Dec 10 2006
Walter, you forgot to change this line in the docs: "The operators =, !, ., &&, ||, ?:, and a few others will likely never be overloadable. " http://www.digitalmars.com/d/operatoroverloading.html : ) L.
Dec 10 2006
Thank you, Walter. D 1.0 is going to be THE BEST general purpose language ... :D
Dec 10 2006
Samuel MV wrote:Thank you, Walter. D 1.0 is going to be THE BEST general purpose language ... :Ds/general purpose \(language\)/\1 EVER/ ...or such :P
Dec 10 2006
Walter Bright wrote:More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zipWalter, this is a great update. Especially the removal of the implicit cast for array pointers. But I'd like to echo the other comments in this thread regarding structs. IMO, we're not there yet. I think folks are looking for a solution that does this: - A ctor like syntax for creating a new struct - No more forced copy of the entire struct on creation - Something that is disambiguated from static opCall - Ctors that are as clear to read as this() in classes and modules. Again, thanks for the solid push to 1.0 this month. I'm sure we haven't seen anything yet. -- - EricAnderton at yahoo
Dec 11 2006
Pragma wrote: <snip>But I'd like to echo the other comments in this thread regarding structs. IMO, we're not there yet. I think folks are looking for a solution that does this: - A ctor like syntax for creating a new struct - No more forced copy of the entire struct on creationWhat do you mean by this?- Something that is disambiguated from static opCall<snip> Do you mean that constructors for structs should have a notation distinct from S(...)? Stewart.
Dec 11 2006
Stewart Gordon wrote:Pragma wrote: <snip>I'm glad you asked. :) Static opCall() is not a ctor. It never was. People have been clamoring to be able to use this() inside of a struct, much like they can with classes and modules. But the desire here goes beyond mere symmetry between type definitions. The forced copy issue is something that is an artifact of emulating a constructor for a struct. Take the standard approach for example: struct Foo{ int a,b,c; } Foo f = {a:1, b:2, c:3}; Foo f = {1,2,3}; // more succinct version So here we create a struct in place, and break encapsulation in the process. What we really want is an opaque type, that has a little more smarts on creation. Taking advantage of in/body/out would be nice too. No problem, we'll just use opCall(): struct Foo{ int a,b,c; static Foo opCall(int a,int b,int c){ Foo _this; _this.a = a; _this.b = b; _this.c = c; return _this; } } Foo f = Foo(1,2,3); That's better, but look at what's really happening here. Inlining and compiler optimization aside, the 'constructor' here creates a Foo on the stack which is then returned and *copied* to the destination 'f'. To most, that won't ever seem like a problem. But for folks who are working with Vector types or Matrix implementations, that's something to scream about. In a nutshell, any struct wider than a register that is populated in the 100's to 1000's is wasting cycles needlessly. So that brings us to something like this: struct Foo{ int a,b,c; this(int a,int b,int c){ this.a = a; this.b = b; this.c = c; } } Foo f = Foo(1,2,3); Ambiguity aside, this fixes encapsulation, gives a familiar syntax, and almost fixes the allocation issues. (see below)But I'd like to echo the other comments in this thread regarding structs. IMO, we're not there yet. I think folks are looking for a solution that does this: - A ctor like syntax for creating a new struct - No more forced copy of the entire struct on creationWhat do you mean by this?Well, I think it's one of the reasons why we don't have ctors for structs right now. The preferred syntax for a "struct ctor" would probably be this: S foo = S(a,b,c); Which is indistinct from "static opCall". Throwing 'new' in there wouldn't work either, since that would be a dynamic allocation: S* foo = new S(a,b,c); So that leaves us with "something else" that provides both a way to invoke a ctor, yet allocates the data on the stack and doesn't force you to create an additional copy: S foo(a,b,c); // c++ style S foo = stackalloc S(a,b,c); // alloca() style (in place of new) S foo = new(stack) S(a,b,c): // another idea -- - EricAnderton at yahoo- Something that is disambiguated from static opCall<snip> Do you mean that constructors for structs should have a notation distinct from S(...)?
Dec 12 2006
"Pragma" <ericanderton yahoo.removeme.com> wrote in message news:elmhtj$1qv7$1 digitaldaemon.com...Well, I think it's one of the reasons why we don't have ctors for structs right now. The preferred syntax for a "struct ctor" would probably be this: S foo = S(a,b,c); Which is indistinct from "static opCall". Throwing 'new' in there wouldn't work either, since that would be a dynamic allocation: S* foo = new S(a,b,c); So that leaves us with "something else" that provides both a way to invoke a ctor, yet allocates the data on the stack and doesn't force you to create an additional copy: S foo(a,b,c); // c++ style S foo = stackalloc S(a,b,c); // alloca() style (in place of new) S foo = new(stack) S(a,b,c): // another ideaPersonally for in-place stack allocation, I like the C++ style the best. It's the simplest to deal with for the compiler (it's 100% obvious that it's being created on the stack and there's no need for optimization to take care of anything). And it makes sense -- it says "make this a local variable, but call the constructor on it as well." There's no assignment so it doesn't look like any copying is going on.
Dec 12 2006
Pragma wrote:Foo f = Foo(1,2,3); That's better, but look at what's really happening here. Inlining and compiler optimization aside, the 'constructor' here creates a Foo on the stack which is then returned and *copied* to the destination 'f'.About this optimization business, is this an issue? Since Walter stated that such copies are optimized away (trivially?), my assumption was that the syntax as it is now relies on this optimization being present. Or to put it in other words, static opCall would not be supported if there was no such optimization possible. Perhaps it is similar to how the use of functors with templates in C++ rely on inlining, STL would be so slow without such optimizations. My question is if it is reasonable to make this assumption or can you put compiler optimization aside?
Dec 12 2006
"Lutger" <lutger.blijdestijn gmail.com> wrote in message news:elmjl8$1tdg$1 digitaldaemon.com...About this optimization business, is this an issue? Since Walter stated that such copies are optimized away (trivially?), my assumption was that the syntax as it is now relies on this optimization being present. Or to put it in other words, static opCall would not be supported if there was no such optimization possible. Perhaps it is similar to how the use of functors with templates in C++ rely on inlining, STL would be so slow without such optimizations. My question is if it is reasonable to make this assumption or can you put compiler optimization aside?The impression I get from Walter is that _eeeevery_ compiler has optimization, so it's a nonissue. :P Optimization should be an entirely optional pass. Making language features rely on it seems hackish at best.
Dec 12 2006
Jarrett Billingsley wrote:"Lutger" <lutger.blijdestijn gmail.com> wrote in message news:elmjl8$1tdg$1 digitaldaemon.com...Exactly. Moreover, it's not always possible to inline or optimize even by a compiler that can do it well, so it *must* be optional by definition. Also there are some rather significant "edge cases" involved here. What about libraries, or reflection? -- - EricAnderton at yahooAbout this optimization business, is this an issue? Since Walter stated that such copies are optimized away (trivially?), my assumption was that the syntax as it is now relies on this optimization being present. Or to put it in other words, static opCall would not be supported if there was no such optimization possible. Perhaps it is similar to how the use of functors with templates in C++ rely on inlining, STL would be so slow without such optimizations. My question is if it is reasonable to make this assumption or can you put compiler optimization aside?The impression I get from Walter is that _eeeevery_ compiler has optimization, so it's a nonissue. :P Optimization should be an entirely optional pass. Making language features rely on it seems hackish at best.
Dec 12 2006
Jarrett Billingsley wrote:"Lutger" <lutger.blijdestijn gmail.com> wrote in message news:elmjl8$1tdg$1 digitaldaemon.com...Can you explain why? 'Rely' in this context doesn't mean the language is broken right? It just means it is slower, but isn't that expected from a non-optimizing compiler anyway?About this optimization business, is this an issue? Since Walter stated that such copies are optimized away (trivially?), my assumption was that the syntax as it is now relies on this optimization being present. Or to put it in other words, static opCall would not be supported if there was no such optimization possible. Perhaps it is similar to how the use of functors with templates in C++ rely on inlining, STL would be so slow without such optimizations. My question is if it is reasonable to make this assumption or can you put compiler optimization aside?The impression I get from Walter is that _eeeevery_ compiler has optimization, so it's a nonissue. :P Optimization should be an entirely optional pass. Making language features rely on it seems hackish at best.
Dec 12 2006
"Lutger" <lutger.blijdestijn gmail.com> wrote in message news:elmkes$1ult$1 digitaldaemon.com...Can you explain why? 'Rely' in this context doesn't mean the language is broken right? It just means it is slower, but isn't that expected from a non-optimizing compiler anyway?Yes, I guess that's true. But if a simple addition i.e. x = a + b; Compiled to mov _TEMP1, a mov _TEMP2, b add _TEMP1, _TEMP2 mov x, _TEMP1 Instead of mov x, a add x, b It'd still be semantically correct, but would it make sense? In the same way, I don't see why the compiler should introduce a needless bit-copy of a (possibly large) structure which can *always* be optimized out when it would be much simpler to skip the bit-copy in the first place. It's an optimization which can always be performed, and so should not be an optimization. It should be the default behavior.
Dec 12 2006
Jarrett Billingsley wrote: <snip>Yes, I guess that's true. But if a simple addition i.e. x = a + b; Compiled to mov _TEMP1, a mov _TEMP2, b add _TEMP1, _TEMP2 mov x, _TEMP1 Instead of mov x, a add x, b It'd still be semantically correct, but would it make sense?Almost. I could see it generate the following code (with optimizations turned off): mov _TEMP, a add _TEMP, b mov x, _TEMP where a, b and x are memory locations and _TEMP is a register. On x86, you can't add the contents of two memory locations, at least one of the values needs to be in a register. With optimizations on, of course, the variables might be stored in registers instead of memory. If so, your proposed code would be legal.
Dec 12 2006
Pragma wrote:That's better, but look at what's really happening here. Inlining and compiler optimization aside, the 'constructor' here creates a Foo on the stack which is then returned and *copied* to the destination 'f'.That problem was solved 15 years ago (for C and C++). I admit that due to sloth I had failed to implement the solution yet in dmd, but it is implemented now and will go out in the next update. There will be NO extra copies. For whatever the merits of opCall vs this(), efficiency is NOT a problem with either, and is not a reason to choose one or the other. The generated code is the same. Under the hood, they are the same. Both take a hidden pointer to where the result is stored. The rest is window dressing.
Dec 12 2006
Walter Bright wrote:Under the hood, they are the same. Both take a hidden pointer to where the result is stored. The rest is window dressing.That means you can't return the result in a register :o). Andrei
Dec 12 2006
Andrei Alexandrescu (See Website For Email) wrote:Walter Bright wrote:That talk can stay on comp.lang.c++.moderated, thank you very much ;-) SeanUnder the hood, they are the same. Both take a hidden pointer to where the result is stored. The rest is window dressing.That means you can't return the result in a register :o).
Dec 12 2006
Andrei Alexandrescu (See Website For Email) wrote:Walter Bright wrote:Actually, it does return it in a register if it fits in one. Try it!Under the hood, they are the same. Both take a hidden pointer to where the result is stored. The rest is window dressing.That means you can't return the result in a register :o).
Dec 13 2006
"Walter Bright" <newshound digitalmars.com> wrote in message news:elnv14$r2d$1 digitaldaemon.com...For whatever the merits of opCall vs this(), efficiency is NOT a problem with either, and is not a reason to choose one or the other. The generated code is the same. Under the hood, they are the same. Both take a hidden pointer to where the result is stored. The rest is window dressing.Alright then. All I've got then is the orthogonality argument. But you won't listen to that either. structs?" and we'll say "you have to use static opCall." And they'll ask "why?" And all we'll be able to do is shake our heads, sigh, and say "I don't know." For the last time, even though you don't care: we have been using static opCall as a WORKAROUND. As in *we never intended for that to be the canonical method of constructing a struct*. I don't think anyone would complain if they were given a consistent, logical method of initializing any aggregate type.
Dec 13 2006
On Wed, 13 Dec 2006 12:02:37 -0500, Jarrett Billingsley <kb3ctd2 yahoo.com> wrote:"Walter Bright" <newshound digitalmars.com> wrote in message news:elnv14$r2d$1 digitaldaemon.com...For the record, I'm fine with static opCall. Structs don't have constructors and destructors, but the language just so happens to allow us to have something that looks like a constructor. We could use Foo.create() to initialize new Foo`s but since opCall is available and looks nice to us, we can go with it. If opCall didn't exist, would we even be having this argument? We would know structs weren't meant to have true constructors and destructors. We have an aesthetically pleasing way to initialize structs, so what's the problem. If your friend asks if structs have constructors, you say no, but since the langauge is so expressive, you have this feature called opCall that makes it just as nice to initialize. P.S. I actually don't want a defensive response to this, I'm just stating my view; thanks.For whatever the merits of opCall vs this(), efficiency is NOT a problem with either, and is not a reason to choose one or the other. The generated code is the same. Under the hood, they are the same. Both take a hidden pointer to where the result is stored. The rest is window dressing.Alright then. All I've got then is the orthogonality argument. But you won't listen to that either. structs?" and we'll say "you have to use static opCall." And they'll ask "why?" And all we'll be able to do is shake our heads, sigh, and say "I don't know." For the last time, even though you don't care: we have been using static opCall as a WORKAROUND. As in *we never intended for that to be the canonical method of constructing a struct*. I don't think anyone would complain if they were given a consistent, logical method of initializing any aggregate type.
Dec 13 2006
On Wed, 13 Dec 2006 12:21:37 -0500, Chris Miller wrote: "We know structs weren't meant to have true constructors and destructors. We have an aesthetically pleasing way to initialize structs, so what's the problem. If your friend asks if structs have constructors, you say no, but since the langauge is so expressive, you have this feature called opCall that makes it just as nice to initialize." *Amen*
Dec 13 2006
Mark Wrenn wrote:On Wed, 13 Dec 2006 12:21:37 -0500, Chris Miller wrote: "We know structs weren't meant to have true constructors and destructors. We have an aesthetically pleasing way to initialize structs, so what's the problem. If your friend asks if structs have constructors, you say no, but since the langauge is so expressive, you have this feature called opCall that makes it just as nice to initialize." *Amen*If structs do add destructors, this can will be reopened - and by that time it will be much more smellier. Andrei
Dec 13 2006
Let's not forget that the members of a struct can be initialized "inline": struct X { int a = 2; } This makes the lack of constructors for structs a lot less significant. Additionally, destructors for structs are primarily used for wrappers, RAII, and let's not forget scope(...), which is much better suited for RAII than destructors ever were. It makes the lack of destructors for structs a lot less significant.. So, if people want a C++-looking initialize function, they can use static opCall, and it'll look like a call to a constructor (ie. having the same name as the struct). L.
Dec 13 2006
Jarrett Billingsley schrieb:"Walter Bright" <newshound digitalmars.com> wrote in message news:elnv14$r2d$1 digitaldaemon.com...To me another big problem is the cast to a struct. How could anyone, who is new to D/ didn't study the spec enough expect, that a cast to a struct is done by calling the "opCall". One of those who came from C++ (like me too) would expect the static opCall as the equivalent to those rather rarely used C++-operator()() overloads. What could one answer if some of them ask why it's like this? Couldn't there just be another name than static opCall? Then, to keep it from being ambigous, there could be a change of the let's say opCreate syntax. It doesn't has to be like in C++, I imagine something in the direction of static struct initializers: struct A { int i, j; } int v = 9; A a = A:{a:10, j:v /*not only constants*/};For whatever the merits of opCall vs this(), efficiency is NOT a problem with either, and is not a reason to choose one or the other. The generated code is the same. Under the hood, they are the same. Both take a hidden pointer to where the result is stored. The rest is window dressing.Alright then. All I've got then is the orthogonality argument. But you won't listen to that either. structs?" and we'll say "you have to use static opCall." And they'll ask "why?" And all we'll be able to do is shake our heads, sigh, and say "I don't know." For the last time, even though you don't care: we have been using static opCall as a WORKAROUND. As in *we never intended for that to be the canonical method of constructing a struct*. I don't think anyone would complain if they were given a consistent, logical method of initializing any aggregate type.
Dec 14 2006
Pragma wrote:Stewart Gordon wrote:If that's not compiled into a direct write (even to the point of keeping the value virtual unless if it's actually needed in contiguous memory) then there's something wrong with the optimiser.Pragma wrote: <snip>I'm glad you asked. :) Static opCall() is not a ctor. It never was. People have been clamoring to be able to use this() inside of a struct, much like they can with classes and modules. But the desire here goes beyond mere symmetry between type definitions. The forced copy issue is something that is an artifact of emulating a constructor for a struct. Take the standard approach for example: struct Foo{ int a,b,c; } Foo f = {a:1, b:2, c:3}; Foo f = {1,2,3}; // more succinct version So here we create a struct in place, and break encapsulation in the process. What we really want is an opaque type, that has a little more smarts on creation. Taking advantage of in/body/out would be nice too. No problem, we'll just use opCall(): struct Foo{ int a,b,c; static Foo opCall(int a,int b,int c){ Foo _this; _this.a = a; _this.b = b; _this.c = c; return _this; } } Foo f = Foo(1,2,3); That's better, but look at what's really happening here. Inlining and compiler optimization aside, the 'constructor' here creates a Foo on the stack which is then returned and *copied* to the destination 'f'.But I'd like to echo the other comments in this thread regarding structs. IMO, we're not there yet. I think folks are looking for a solution that does this: - A ctor like syntax for creating a new struct - No more forced copy of the entire struct on creationWhat do you mean by this?To most, that won't ever seem like a problem. But for folks who are working with Vector types or Matrix implementations, that's something to scream about. In a nutshell, any struct wider than a register that is populated in the 100's to 1000's is wasting cycles needlessly.I've never liked doing that - if you're going to have very large vectors or matrices, it's usually better just to switch to a programmatic model (run-time sized) rather than keeping it parametric (templated). At some point you're spending more time compiling than you are executing. This requires duplication of a lot of code, which is unacceptable. I've been thinking about this problem for a long time and I still have no solution to it (mixins are so very not the right way); perhaps we're misconsidering how parametric and programmatic types should interact. Let's say that the values used in the constructor of a type are recorded. So we might have (excuse the language): #using: #moki.(size, static array); Matrix := #class { data : static array of (type, rows, cols); // No content needed. #this (type, rows : size, cols : size); }; Now "rows" and "cols" are both automatically constant properties of any created Matrix. If our algorithm requires certain limitations on the matrix, we can "specialise" it: transpose (matrix : Matrix (type, rows, rows)) : Matrix (type, rows, rows) ... And the compiler can generally optimise the Matrix like it were parametric - it could even apply discretionary optimisation so that it doesn't waste compile time on what doesn't even matter - but you could still use it programmatically.So that brings us to something like this: struct Foo{ int a,b,c; this(int a,int b,int c){ this.a = a; this.b = b; this.c = c; } } Foo f = Foo(1,2,3); Ambiguity aside, this fixes encapsulation, gives a familiar syntax, and almost fixes the allocation issues. (see below)There's still an implied copy, but again, that shouldn't be relevant for any properly-working optimiser."new <struct>" didn't used to parse, and I argued then that it shouldn't because everywhere else the "new" operator exactly described the type it would create. If this special case were removed, it could work as a constructor call. But really I think static opCall should just be killed off.Well, I think it's one of the reasons why we don't have ctors for structs right now. The preferred syntax for a "struct ctor" would probably be this: S foo = S(a,b,c); Which is indistinct from "static opCall". Throwing 'new' in there wouldn't work either, since that would be a dynamic allocation: S* foo = new S(a,b,c);- Something that is disambiguated from static opCall<snip> Do you mean that constructors for structs should have a notation distinct from S(...)?So that leaves us with "something else" that provides both a way to invoke a ctor, yet allocates the data on the stack and doesn't force you to create an additional copy: S foo(a,b,c); // c++ style S foo = stackalloc S(a,b,c); // alloca() style (in place of new) S foo = new(stack) S(a,b,c): // another idea
Dec 21 2006
http://digitalmars.com/d/attribute.html#align -- AlignAttribute: align align ) Integer ) -- I guess this was meant to be: -- AlignAttribute: align align ( Integer ) -- Apart from that .. I just found several new things in the documentation, scary. :P
Dec 12 2006
Walter Bright wrote:More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zipLooks like casts from void* to struct* is broken. Russ
Dec 15 2006
Russ Lewis wrote:Walter Bright wrote:Not sure what I did wrong or what I'm doing right now, but they seem to be working. Sorry for any confusion I caused.More ABI changes, and implicit [] => * no longer allowed. http://www.digitalmars.com/d/changelog.html http://ftp.digitalmars.com/dmd.175.zipLooks like casts from void* to struct* is broken. Russ
Dec 21 2006