digitalmars.D - const challenge
- Jason House (5/5) Jan 30 2008 It really seems like the whole const debate has died down and Walter is ...
- Jason House (12/13) Jan 30 2008 Here's an example I remember seeing posted. IMHO, it sounds like a rath...
- Janice Caron (20/21) Jan 30 2008 Actually, I think it can. You just have to cheat a bit:
- Steven Schveighoffer (15/37) Jan 31 2008 The obvious solution is to store the execution log outside the A instanc...
- Jason House (4/6) Jan 31 2008 I made up the scenario based on what I remember from the const debates. ...
- Steven Schveighoffer (19/41) Jan 31 2008 I remember this example, and when I saw it, I agreed with the poster, th...
- Jason House (3/15) Jan 31 2008 What I have nagging me in the back of my mind is if there should be ways...
- Janice Caron (31/31) Jan 31 2008 OK, here's an example, borrowed a bit from Tango.
- Georg Wrede (6/44) Jan 31 2008 Unique sounds interesting. And new to me, at least as a definable concep...
- Janice Caron (8/13) Jan 31 2008 Coincidentally, Andrei posted about this very thing just today.
- Steven Schveighoffer (6/19) Jan 31 2008 char[] niftyStringFunction(char[] outBuffer, const(char)[] input)
- Kris (36/60) Jan 31 2008 Steve - some D2 questions for ya:
- Janice Caron (41/52) Feb 01 2008 It indicates a difficulty, yes. If something is invariant(T), then it
- Sean Kelly (15/51) Feb 01 2008 That's ridiculous. Is there an implicit cast here as well:
- Kris (7/60) Feb 01 2008 Hear hear, Sean ...
- Janice Caron (19/25) Feb 01 2008 Has anyone said they're considered complete? I don't think anyone
- Sean Kelly (38/59) Feb 01 2008 Yes, thanks to Steven's bug report. What bothers me is that it
-
Walter Bright
(14/25)
Feb 01 2008
There is a lot more coming
. For example, I'm pretty excited about - Sean Kelly (6/10) Feb 02 2008 I'll admit that I have my trepidations about invariant strings, because
- Sergey Gromov (4/12) Feb 03 2008 There wouldn't be any need in unique() if not invariant(). Right now pr...
- Oskar Linde (51/56) Feb 01 2008 I can't help but agree. The very first thing I tried when D 2.009 was
- bearophile (8/14) Feb 01 2008 I have read this post of yours and I agree with most things you say.
- Walter Bright (18/79) Feb 02 2008 The issue here is assigning a mutable instance of Vector to an
- Steven Schveighoffer (21/41) Feb 04 2008 I just wanted to respond to this one point.
- Walter Bright (3/23) Feb 05 2008 Your argument is sound, and I agree with it. I think it's pretty clear
- Steven Schveighoffer (17/50) Feb 01 2008 This would compile. Remember, mutable arrays are implicitly castable to...
- Jason House (2/4) Feb 01 2008 I believe that's tail const, not head const.
- Steven Schveighoffer (3/7) Feb 01 2008 Yes of course, you are correct, my bad :)
- Kris (14/60) Feb 01 2008 If implicit, as you say, the sugar would be redundant. On the other hand...
- Sergey Gromov (16/58) Feb 01 2008 Anything casts to 'const' implicitly because 'const' doesn't violate any
- Kris (2/18) Feb 01 2008 Thank you, SnakE, for clarifying that. I'd been harbouring a misguided i...
- Sean Kelly (7/40) Feb 01 2008 This is one area where I'd like to have head const, as I think it would
- Sergey Gromov (21/23) Feb 01 2008 Yes you're right. It's an attempt to avoid undefined behavior in
- Sergey Gromov (5/30) Feb 01 2008 Of course:
- Janice Caron (24/30) Feb 01 2008 Only function returns, I think.
- Walter Bright (7/7) Feb 02 2008 There are some research papers about unique as a type qualifier, and it
- Janice Caron (27/34) Feb 02 2008 Yep, you're spot on the nail. Nobody can argue with any of those
- Derek Parnell (10/28) Feb 02 2008 No, you don't have to do that.
- Janice Caron (5/14) Feb 02 2008 No it doesn't. The error message is:
- Derek Parnell (24/43) Feb 02 2008 Ok, so maybe I'm /am/ going mad ... but this is what happens on my machi...
- Janice Caron (10/24) Feb 02 2008 I'm fairly sure that the char[] version didn't compile in some earlier
- Derek Parnell (19/46) Feb 03 2008 Of course it doesn't. As far as I can recall, D has always said that
- Janice Caron (13/16) Feb 03 2008 I don't think that's true. Back in November, Steven Schveighoffer
- Derek Parnell (12/24) Feb 03 2008
- Steven Schveighoffer (20/41) Feb 03 2008 I probably should respond to this :)
- Steven Schveighoffer (9/57) Feb 04 2008 I just tested this out in D 2.006, and you are correct. a1 ~ "\n" resul...
- Derek Parnell (10/20) Feb 04 2008 Yes, and another point is why does the compiler treat
- Walter Bright (4/11) Feb 02 2008 I understand that's a problem. The solution, however, doesn't need
- Janice Caron (3/5) Feb 02 2008 Nice!
- Janice Caron (4/6) Feb 02 2008 Will we be able to write our own functions which are declared to
- Walter Bright (2/8) Feb 02 2008 No. They're basically a hack.
- Robert Fraser (2/11) Feb 03 2008 We can do it with macros, though, right?
- Walter Bright (2/14) Feb 03 2008 Yes.
- Russell Lewis (19/28) Feb 03 2008 If we had a opImplicitCast (and it was overloadable), then it would be
- Janice Caron (4/6) Feb 03 2008 It would - but you'd quickly find ambiguities, at which the compiler
- Russell Lewis (8/15) Feb 04 2008 Can you expand on this?
- Janice Caron (8/17) Feb 05 2008 Sure. If one function, f, returned PolysemousContainer(A,B), and
- Russell Lewis (10/31) Feb 05 2008 Ok, good point. But, IMHO, that isn't a good argument against
- Sean Kelly (3/15) Feb 02 2008 Awesome. I was wondering if that's where we were going with this.
- Christopher Wright (8/20) Feb 03 2008 More simply, catenation of a mutable variable with a non-mutable one
- Janice Caron (5/11) Feb 03 2008 Except that "non-mutable" doesn't necessarily mean const, and
- Christopher Wright (6/14) Feb 03 2008 My suggestion was:
- Janice Caron (2/3) Feb 02 2008 I meant, unnecessary dups.
- Janice Caron (5/11) Feb 02 2008 That said, we could always have a concatenation function in phobos:
- Christopher Wright (2/16) Feb 02 2008 Which is silly, when we have a catenation operator in the language.
- Janice Caron (5/12) Feb 02 2008 Not really. We could have two functions, cat and icat, whereby cat
- Christopher Wright (20/24) Feb 03 2008 The issue with the concatenation operator is that its operands need not
- Janice Caron (16/17) Feb 01 2008 In D1, it's /exactly/ what you want.
- Steven Schveighoffer (23/32) Feb 01 2008 D1 does not have this type of function signature possible, I was talking...
- Joel C. Salomon (13/18) Jan 31 2008 -----BEGIN PGP SIGNED MESSAGE-----
- Sergey Gromov (14/34) Feb 01 2008 Inline array initialization is also an expression, and unique must survi...
- Janice Caron (9/21) Feb 01 2008 What I'm telling the compiler here is that the function's formal
- Sergey Gromov (7/23) Feb 01 2008 On second thoughts, why would you want a function with a unique
- Janice Caron (29/36) Feb 01 2008 So that you can do
- Sergey Gromov (7/16) Feb 03 2008 Something tells me that this use case is very rare, but the dangers are
- Steven Schveighoffer (5/16) Feb 03 2008 Can't you just declare the functions as accepting mutable strings? Does...
- Sergey Gromov (5/12) Feb 01 2008 The possibility to cast should still be there for complex cases where yo...
- Janice Caron (18/21) Feb 01 2008 I disagree. For type safety, it should be required wherever a
- Janice Caron (3/8) Feb 01 2008 Actually, I just realised I might be agreeing (not disagreeing) with
It really seems like the whole const debate has died down and Walter is moving on to bigger and better things. This is great to see, but now I want to stress the design! I have two challenges, and they're at direct opposites of each other. 1. Create a usage example that can only be solved by stripping const out, or adopting bad coding practices (global variables?). I suspect this may bring back old examples about why transitive const is bad. Please avoid flames such as "const is useless" or "my favorite language does not have const and it's great" I really hope this does not boil down to useless arguing. I'm doing this in the hopes that we can finally set all this const stuff to rest and find great things to put into some kind of const FAQ. If that's not possible, I hope this will help highlight what remaining issues exist and how acceptable they are.
Jan 30 2008
Jason House Wrote:1. Create a usage example that can only be solved by stripping const out, or adopting bad coding practices (global variables?). I suspect this may bring back old examples about why transitive const is bad. Please avoid flames such as "const is useless" or "my favorite language does not have const and it's great"Here's an example I remember seeing posted. IMHO, it sounds like a rather annoying thing to handle. It can be solved by stripping out the const references to class A or possibly replacing "executionLog x" with some global variable reference. I could imagine this being problematic with multi-threaded or other large applications that have distinct logs for different components/threads. class A{ version(debug){ executionLog x; } ... // includes some writing to log for debugging } class B{ const class A; // can't work while debugging ... }
Jan 30 2008
On Jan 30, 2008 10:09 PM, Jason House <jason.james.house gmail.com> wrote:const class A; // can't work while debuggingActually, I think it can. You just have to cheat a bit: debug struct executionLog { const void function1(/*...*/) { /* ... */ } const void function2(/*...*/) { /* ... */ } const void function3(/*...*/) { /* ... */ } ... private: /* all variables */ } If the member functions need to modify the member variables, they will need to cast away const. Yes, that is cheating, and undefined, but (a) at least all the cheating is localised to one place, so the rest of your code maintains its const correctness, and (b) the undefined behavior will only occur in your debug build, so if works on your platform, it really doesn't matter what it will do on anyone else's, since they will only see the release version. (Also, (c) - it's a struct, not a class, so all the member variables /will/ be on the stack, so the possibility of undefined behavior is minimised anyway).
Jan 30 2008
"Jason House" wroteJason House Wrote:The obvious solution is to store the execution log outside the A instance. However, I'm not sure how you use it, or if you need to have multiple instances of x. Normally logging is done through a static/global variable, i.e.: class A { version(debug){ static executionLog x; static this() { x = getExecutionLogForThisClass(); } } } -Steve1. Create a usage example that can only be solved by stripping const out, or adopting bad coding practices (global variables?). I suspect this may bring back old examples about why transitive const is bad. Please avoid flames such as "const is useless" or "my favorite language does not have const and it's great"Here's an example I remember seeing posted. IMHO, it sounds like a rather annoying thing to handle. It can be solved by stripping out the const references to class A or possibly replacing "executionLog x" with some global variable reference. I could imagine this being problematic with multi-threaded or other large applications that have distinct logs for different components/threads. class A{ version(debug){ executionLog x; } ... // includes some writing to log for debugging } class B{ const class A; // can't work while debugging ... }
Jan 31 2008
Steven Schveighoffer Wrote:However, I'm not sure how you use it, or if you need to have multiple instances of x.I made up the scenario based on what I remember from the const debates. I don't have the original post or remember who posted it. I was really hoping for those who regularly use const-based design patterns in other languages to jump in with examples that exist in their code. I hoped that me providing an example might inspire others to add more examples. I unfortunately don't have sufficient appreciation for the original poster's feeling to do it justice. I also recall some argument about handling widget draw routines. If they're a constant object but need to draw themselves on something. I haven't done any detailed GUI programming outside of java beans or GTK, so I tried to stay away from it and hope someone else would post it. I've at least done some logging and can appreciate problems with multi-threading. Mostly, I feel like the const debate has really died down (probably a good thing), but now we need to open the door once more to ensure potential users are happy with the const design. I have to imagine there's still people out there that think transitive const is a bad thing and still others that think transitive const is the best thing since sliced bread. I'd really love to see them jump into this thread and provide some real examples.
Jan 31 2008
"Jason House" wroteSteven Schveighoffer Wrote:I remember this example, and when I saw it, I agreed with the poster, that transitive const makes this type of thing more convenient. However, I've proven that transitive const is a subet of const, and therefore, not necessary. If something is transitive const, it means that it is partially const, meaning it's not const. I believe that there is always a way to redesign your thinking to have const work. If you have a const widget and need to have it draw itself, pass in the non-const values needed to do the drawing. Or if you absolutely have to have a structure with a draw routine that doesn't take the argument of the thing to draw it on, then create a wrapper structure that isn't const but contains the const widget. Or change your widget so the data you want to remain const is declared const. It's hard to speculate without seeing the actual code, but I believe it's possible to solve.However, I'm not sure how you use it, or if you need to have multiple instances of x.I made up the scenario based on what I remember from the const debates. I don't have the original post or remember who posted it. I was really hoping for those who regularly use const-based design patterns in other languages to jump in with examples that exist in their code. I hoped that me providing an example might inspire others to add more examples. I unfortunately don't have sufficient appreciation for the original poster's feeling to do it justice. I also recall some argument about handling widget draw routines. If they're a constant object but need to draw themselves on something. I haven't done any detailed GUI programming outside of java beans or GTK, so I tried to stay away from it and hope someone else would post it. I've at least done some logging and can appreciate problems with multi-threading.Mostly, I feel like the const debate has really died down (probably a good thing), but now we need to open the door once more to ensure potential users are happy with the const design. I have to imagine there's still people out there that think transitive const is a bad thing and still others that think transitive const is the best thing since sliced bread. I'd really love to see them jump into this thread and provide some real examples.I think the transitive const debate has died down. I still believe the head-const debate is very much alive, and I'm prepared to argue my side if anyone should challenge that validity. Last I checked, nobody had refuted my points that head-const for a class was possible and useful :) -Steve
Jan 31 2008
Steven Schveighoffer Wrote:I believe that there is always a way to redesign your thinking to have const work. If you have a const widget and need to have it draw itself, pass in the non-const values needed to do the drawing. Or if you absolutely have to have a structure with a draw routine that doesn't take the argument of the thing to draw it on, then create a wrapper structure that isn't const but contains the const widget. Or change your widget so the data you want to remain const is declared const. It's hard to speculate without seeing the actual code, but I believe it's possible to solve.That's the whole point of my challenge. I'd like to see people post their real code and have others tack a whack at redesigning them. Maybe we'll see it's easy and there's a general method that should go into an FAQ. Or maybe there's a few corner cases that are tough to solve. Either way, we're smarter about the pros and cons of our (now stable?) const design.I think the transitive const debate has died down. I still believe the head-const debate is very much alive, and I'm prepared to argue my side if anyone should challenge that validity. Last I checked, nobody had refuted my points that head-const for a class was possible and useful :)What I have nagging me in the back of my mind is if there should be ways to break transitive const with an instance of head const... such as access to a logging utility, a raster for drawing images, etc... I would like to put that to rest. All my current coding is in D 1.x. I'd love to see active (C++?) const coders to post problematic cases. In the end, I'd like to see a final blessing of D's const and have stuff like Tango finally start using it (so I can then port my code that depends on Tango to D 2.x)
Jan 31 2008
OK, here's an example, borrowed a bit from Tango. char[] niftyStringFunction(char[] outBuffer, char[] input) { /*...*/ } The idea is, if (outBuffer == null) then the function allocates memory and returns it; whereas, if (outBuffer != null) then the result is writen into outBuffer, and the return value is a slice of outBuffer. (The idea is to minimize allocations). Converting this to D2 is problematic, because you want to return a string, but string is invariant, so if you do that, you won't be able to pass the same buffer back into other, similarly nifty string functions. The interesting thing about this sort of function is that there is an implicit in-contract that outBuffer, if supplied, must be unique (the caller must have finished with it), and an implicit out-countract that the return value is unique (even if you return outBuffer, because of the implicit in-contract), so you /could/ code your way around this by returning a string, and then casting away the invariance if you want to reuse the buffer - but that is a nasty kludge. I can come up with const-correct workarounds which do the job, but none which do the job /as efficiently/. Once again, this problem arises because D has no way to express uniqueness. If we had such a means, then the const-correct prototype would be unique(char)[] niftyStringFunction(unique(char)[] outBuffer, string input) The workaround is simple enough though: just don't use const at this level. Treat functions of this ilk as a low-level API layer. Then supply a higher-level layer with more conventional semantics, which hides the low-level layer from the caller. All the const casting can go on in the interface between the two layers.
Jan 31 2008
Janice Caron wrote:OK, here's an example, borrowed a bit from Tango. char[] niftyStringFunction(char[] outBuffer, char[] input) { /*...*/ } The idea is, if (outBuffer == null) then the function allocates memory and returns it; whereas, if (outBuffer != null) then the result is writen into outBuffer, and the return value is a slice of outBuffer. (The idea is to minimize allocations). Converting this to D2 is problematic, because you want to return a string, but string is invariant, so if you do that, you won't be able to pass the same buffer back into other, similarly nifty string functions. The interesting thing about this sort of function is that there is an implicit in-contract that outBuffer, if supplied, must be unique (the caller must have finished with it), and an implicit out-countract that the return value is unique (even if you return outBuffer, because of the implicit in-contract), so you /could/ code your way around this by returning a string, and then casting away the invariance if you want to reuse the buffer - but that is a nasty kludge. I can come up with const-correct workarounds which do the job, but none which do the job /as efficiently/. Once again, this problem arises because D has no way to express uniqueness. If we had such a means, then the const-correct prototype would be unique(char)[] niftyStringFunction(unique(char)[] outBuffer, string input) The workaround is simple enough though: just don't use const at this level. Treat functions of this ilk as a low-level API layer. Then supply a higher-level layer with more conventional semantics, which hides the low-level layer from the caller. All the const casting can go on in the interface between the two layers.Unique sounds interesting. And new to me, at least as a definable concept. Question: suppose you have something defined as unique, and then somebody makes a copy of it. What happens to the uniqueness? (You may want to explain this even more broadly if you know that most people have had problems getting some specific part of the idea.)
Jan 31 2008
On 1/31/08, Georg Wrede <georg nospam.org> wrote:Unique sounds interesting. And new to me, at least as a definable concept. Question: suppose you have something defined as unique, and then somebody makes a copy of it. What happens to the uniqueness? (You may want to explain this even more broadly if you know that most people have had problems getting some specific part of the idea.)Coincidentally, Andrei posted about this very thing just today. http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.bugs&article_id=13195 He wrote Unique!(T) where I wrote unique(T). Uniqueness isn't something the compiler can check, so the idea is to make it the user's responsibility. Simply put, unique(T) can implicitly cast to T, const(T) or invariant(T). But woe betide any irresponsible programmer who declares something unique when it isn't!
Jan 31 2008
"Janice Caron" wroteOK, here's an example, borrowed a bit from Tango. char[] niftyStringFunction(char[] outBuffer, char[] input) { /*...*/ } The idea is, if (outBuffer == null) then the function allocates memory and returns it; whereas, if (outBuffer != null) then the result is writen into outBuffer, and the return value is a slice of outBuffer. (The idea is to minimize allocations). Converting this to D2 is problematic, because you want to return a string, but string is invariant, so if you do that, you won't be able to pass the same buffer back into other, similarly nifty string functions.char[] niftyStringFunction(char[] outBuffer, const(char)[] input) The function contract says, "I may write to outBuffer, I won't write to input. You can write to the result if you wish". How is this not what you want? -Steve
Jan 31 2008
Steve - some D2 questions for ya: no nesting/chaining of results permitted? That would be a problem :) I find the current const approach to exhibit various opposing tensions, so I'm not exactly a 'fan' at this time. Taking an example related to the discussion, and if I understand correctly, the variations on Unique!(T) are little more than attempts to hide a cast() operation under a sugary coating, right? A need for such sugar in the language syntax would surely tend to indicate a notable flaw? As far as the example api-style goes, here's hoping that this will work correctly in D2: where 'in' indicates only that the input will not be modified by the callee. By doing so, the following should be clean: Given that the input cannot be modified, you should also be able to pass a const or invariant string as an 'in' parameter; right? If that's the case, then ok. Assuming all is kosher so far, sliced 'in' arguments should also be accepted in the same fashion: right? Additionally, the optional output should always be good for use with slices also: yes? - Kris "Steven Schveighoffer" <schveiguy yahoo.com> wrote in message news:fnu30k$2fnm$1 digitalmars.com..."Janice Caron" wroteOK, here's an example, borrowed a bit from Tango. char[] niftyStringFunction(char[] outBuffer, char[] input) { /*...*/ } The idea is, if (outBuffer == null) then the function allocates memory and returns it; whereas, if (outBuffer != null) then the result is writen into outBuffer, and the return value is a slice of outBuffer. (The idea is to minimize allocations). Converting this to D2 is problematic, because you want to return a string, but string is invariant, so if you do that, you won't be able to pass the same buffer back into other, similarly nifty string functions.char[] niftyStringFunction(char[] outBuffer, const(char)[] input) The function contract says, "I may write to outBuffer, I won't write to input. You can write to the result if you wish". How is this not what you want? -Steve
Jan 31 2008
On Feb 1, 2008 4:55 AM, Kris <foo bar.com> wrote:Steve - some D2 questions for ya: Taking an example related to the discussion, and if I understand correctly, the variations on Unique!(T) are little more than attempts to hide a cast() operation under a sugary coating, right?Yes.A need for such sugar in the language syntax would surely tend to indicate a notable flaw?It indicates a difficulty, yes. If something is invariant(T), then it /may/ be in a read-only segment, and so modifying it is undefined. That's fair enough. The thing is though that one can create a buffer full of stuff on the heap or the stack and then cast it to invariant. (That's what assumeUnique() does - it's more or less template sugar for cast(invariant)). Now you can pass all those objects to functions that take invariants, no problem. (At least, no problem if you don't mind explicit casts. Some people object to them). Unfortunately, it does have a negative consequence, which is exemplified in concatenation. To get a mutable buffer when concatenating, you have to do something like this: string s; char[] t; char[] buffer = cast(char[])("hello " ~ s ~ cast(invariant)t); This is known to be safe. Unfortunately, the D specification says that casting away invariance is undefined, so strictly speaking, one shouldn't do it. Perhaps, in the future, Andrei may come up with some clever library template to hide the cast, but it will still be there, under the hood. So the advantage of the Unique!() type would be that concatenation of strings could be allowed to return Unique!(char)[], which would mean that, together with other changes like implicitly casting all the supplied arguments to const, one would then be able to write: string s; char[] t; char[] buffer ="hello " ~ s ~ t; so you wouldn't necessarily actually /see/ the Unique!() types - they'd just be there to make the code compile.As far as the example api-style goes, here's hoping that this will work correctly in D2:Everything works correctly in D2. The question is, will it work as expected? Will it provide the right API to allow calling code to use it? Will it demand additional explicit casts? If the philosopy is "explicit casts are bad", then what we have right now is not sufficient.<snip> yes?I don't see anything wrong with anything you said. It just doesn't provide the right API to interface with higher layers, that's all. Some functions will call this like string result = other ("foo" ); and get a compile error. Sure - there are workarounds. That's really the whole point of this thread though.
Feb 01 2008
Janice Caron wrote:On Feb 1, 2008 4:55 AM, Kris <foo bar.com> wrote:That's ridiculous. Is there an implicit cast here as well: int x = 1 + 2; And does the compiler complain about this: int y = 3 + x; This second example is exactly the same as your example above: char[] buffer = "hello " ~ t; Requiring casts here seems like a blatant flaw in the current design. I'm aware of the counter-example Andrei posted, but that doesn't change the fact that the above should have always worked. Quite frankly, I'm shocked that the const features were considered complete with something like this in place. One of these days I really need to spend some time with 2.0. I'm starting to wonder if no one has any issues with it simply because no one's using it. SeanSteve - some D2 questions for ya: Taking an example related to the discussion, and if I understand correctly, the variations on Unique!(T) are little more than attempts to hide a cast() operation under a sugary coating, right?Yes.A need for such sugar in the language syntax would surely tend to indicate a notable flaw?It indicates a difficulty, yes. If something is invariant(T), then it /may/ be in a read-only segment, and so modifying it is undefined. That's fair enough. The thing is though that one can create a buffer full of stuff on the heap or the stack and then cast it to invariant. (That's what assumeUnique() does - it's more or less template sugar for cast(invariant)). Now you can pass all those objects to functions that take invariants, no problem. (At least, no problem if you don't mind explicit casts. Some people object to them). Unfortunately, it does have a negative consequence, which is exemplified in concatenation. To get a mutable buffer when concatenating, you have to do something like this: string s; char[] t; char[] buffer = cast(char[])("hello " ~ s ~ cast(invariant)t); This is known to be safe. Unfortunately, the D specification says that casting away invariance is undefined, so strictly speaking, one shouldn't do it. Perhaps, in the future, Andrei may come up with some clever library template to hide the cast, but it will still be there, under the hood.
Feb 01 2008
Hear hear, Sean ... Explicit casts are for exceptional conditions, when you're deliberately working against the compiler for one reason or another. Walter has noted this (in one form or another) on various ocassions, so it's not as though he thinks cast() should be a common pedestrian requirement. "Sean Kelly" <sean f4.ca> wrote in message news:fnvgm4$2akd$1 digitalmars.com...Janice Caron wrote:On Feb 1, 2008 4:55 AM, Kris <foo bar.com> wrote:That's ridiculous. Is there an implicit cast here as well: int x = 1 + 2; And does the compiler complain about this: int y = 3 + x; This second example is exactly the same as your example above: char[] buffer = "hello " ~ t; Requiring casts here seems like a blatant flaw in the current design. I'm aware of the counter-example Andrei posted, but that doesn't change the fact that the above should have always worked. Quite frankly, I'm shocked that the const features were considered complete with something like this in place. One of these days I really need to spend some time with 2.0. I'm starting to wonder if no one has any issues with it simply because no one's using it. SeanSteve - some D2 questions for ya: Taking an example related to the discussion, and if I understand correctly, the variations on Unique!(T) are little more than attempts to hide a cast() operation under a sugary coating, right?Yes.A need for such sugar in the language syntax would surely tend to indicate a notable flaw?It indicates a difficulty, yes. If something is invariant(T), then it /may/ be in a read-only segment, and so modifying it is undefined. That's fair enough. The thing is though that one can create a buffer full of stuff on the heap or the stack and then cast it to invariant. (That's what assumeUnique() does - it's more or less template sugar for cast(invariant)). Now you can pass all those objects to functions that take invariants, no problem. (At least, no problem if you don't mind explicit casts. Some people object to them). Unfortunately, it does have a negative consequence, which is exemplified in concatenation. To get a mutable buffer when concatenating, you have to do something like this: string s; char[] t; char[] buffer = cast(char[])("hello " ~ s ~ cast(invariant)t); This is known to be safe. Unfortunately, the D specification says that casting away invariance is undefined, so strictly speaking, one shouldn't do it. Perhaps, in the future, Andrei may come up with some clever library template to hide the cast, but it will still be there, under the hood.
Feb 01 2008
On 2/1/08, Sean Kelly <sean f4.ca> wrote:Requiring casts here seems like a blatant flaw in the current design.I agree. But I gather Andrei, at least, is working on fixing it.Quite frankly, I'm shocked that the const features were considered complete with something like this in place.Has anyone said they're considered complete? I don't think anyone considers const to be finalized yet. It's still a work in progress, and there are still one or two niggles remaining, which get brought up from time to time. I guess we all assume that the wrinkles will get ironed out in time.I'm starting to wonder if no one has any issues with it simply because no one's using it.Ah, now there I have to say you're wrong on both counts. There are still issues. Not as many as before, but still some minor ones. They do get reported - sometimes here, sometimes in bugzilla. And there are plenty of people using it, too (me, for one). That's how I know about the concatenation thing. I think it's more that the "cutting edge brigade" /expect/ things to have a few problems, and so aren't that distressed when we encounter them. We just figure out workarounds, and then post about it. Eventually, the workaround stops being needed. (...we would hope). I have to say, I love D2. I love the const features. I can live with the annoyances (for now) because I think we're working towards something marvellous.
Feb 01 2008
Janice Caron wrote:On 2/1/08, Sean Kelly <sean f4.ca> wrote:Yes, thanks to Steven's bug report. What bothers me is that it apparently took a bug report and a follow-up argument to get this addressed. I'm all for making things illegal until there is a substantive argument for why they should not be, but this particular feature is so fundamental that I'm quite honestly shocked that it was left un-addressed.Requiring casts here seems like a blatant flaw in the current design.I agree. But I gather Andrei, at least, is working on fixing it.Walter said so to me privately recently. I asked him about it because I didn't want to devote the time to porting Tango if there was any chance that the design may change.Quite frankly, I'm shocked that the const features were considered complete with something like this in place.Has anyone said they're considered complete?I don't think anyone considers const to be finalized yet. It's still a work in progress, and there are still one or two niggles remaining, which get brought up from time to time. I guess we all assume that the wrinkles will get ironed out in time.See above. However, in this particular case, changing the way these expressions are handled won't break any code. It will simply eliminate the necessity for cast operations. So I suppose one could argue that the syntax has been finalized but the implementation may still be tweaked a bit.I think it's more that the "cutting edge brigade" /expect/ things to have a few problems, and so aren't that distressed when we encounter them. We just figure out workarounds, and then post about it. Eventually, the workaround stops being needed. (...we would hope).I suppose I've always been a bit different then. If I encounter something that seems broken I'll generally avoid it until it works properly. Traditionally, I would have posted a proposal or critique about it as well, but I've become a bit jaded about this over time. At this point it's more likely that I'll just spend the hour working on something productive rather than writing a long post that will be probably be ignored (this is how I feel, even if it isn't true).I have to say, I love D2. I love the const features. I can live with the annoyances (for now) because I think we're working towards something marvellous.To be honest, I have trepidations about 2.0, largely for what many would feel are silly reasons. For example, I simply don't find the aesthetics of the new features appealing. And while many might say this aesthetics are unimportant, they are a significant reason why I initially switched to D from C++. For the rest, I am mostly waiting for 2.0 to get some of the features that I actually care about. Being a long-time C++ person one might think that I would consider const to be tremendously important, but experience with it has mostly made me feel that const is often more trouble than it's worth. In large projects, I more often find myself struggling against other programmer's inconsistent choices of how to apply const to their designs than I do benefiting from any apparent security that it provides. That said, I do think that the choice for transitive const in D will help tremendously, because it mandates consistency of design. Sean
Feb 01 2008
Sean Kelly wrote:For the rest, I am mostly waiting for 2.0 to get some of the features that I actually care about.There is a lot more coming <g>. For example, I'm pretty excited about the upcoming destructors for structs - they're an earthquake.Being a long-time C++ person one might think that I would consider const to be tremendously important, but experience with it has mostly made me feel that const is often more trouble than it's worth. In large projects, I more often find myself struggling against other programmer's inconsistent choices of how to apply const to their designs than I do benefiting from any apparent security that it provides. That said, I do think that the choice for transitive const in D will help tremendously, because it mandates consistency of design.I suppose everyone knows how I feel about C++ const - it's the germ of a good idea, but it's a feature that relies more on programmer convention rather than language enforcement. This makes it more trouble than it's worth. D const, on the other hand, is driven by the idea that the semantics of it must be reliable. The twin concepts of invariantness and transitivity are the key. As we discussed, I think it results in a paradigm change, rather than just sprinkling const through the declarations. Invariant strings are one example of the transformative nature of it, and it took me a long time to 'get it'. So I understand completely the skepticism about it.
Feb 01 2008
Walter Bright wrote:Invariant strings are one example of the transformative nature of it, and it took me a long time to 'get it'. So I understand completely the skepticism about it.I'll admit that I have my trepidations about invariant strings, because it seems that they could cause a great deal of GC churn that may be avoided with in-place modification. But perhaps this is simply an unreasonable fear. Sean
Feb 02 2008
Walter Bright Wrote:D const, on the other hand, is driven by the idea that the semantics of it must be reliable. The twin concepts of invariantness and transitivity are the key. As we discussed, I think it results in a paradigm change, rather than just sprinkling const through the declarations. Invariant strings are one example of the transformative nature of it, and it took me a long time to 'get it'. So I understand completely the skepticism about it.There wouldn't be any need in unique() if not invariant(). Right now programmers have to rely on conventions and their knowledge of data structures rather than on strict typing. SnakE
Feb 03 2008
Sean Kelly wrote:Quite frankly, I'm shocked that the const features were considered complete with something like this in place. One of these days I really need to spend some time with 2.0. I'm starting to wonder if no one has any issues with it simply because no one's using it.I can't help but agree. The very first thing I tried when D 2.009 was released was: struct Vector { int x,y; } invariant Vector up = {0,1}; void main() { Vector y = up; } which didn't work because: Error: no property 'opCall' for type 'Vector' ??? No idea what that means. Is it trying to implicitly call a non-existant struct static opCall "constructor"? and changing the main body to: Vector y; y = up; Yields: Error: cannot implicitly convert expression (up) of type invariant(Vector) to Vector Declaring constants using the "const" keyword is of course(?) wrong, and: (I use the function call to illustrate the point, as just initializing a variable gives the weird opCall error) const Vector c = {1,1}; void foo(invariant Vector v) {} void main() { foo(c); } Yields: Error: cannot implicitly convert expression (c) of type const(Vector) to invariant(Vector) And changing that to a "manifest constant": enum Vector c = {1,1}; Yields: Error: cannot implicitly convert expression (c) of type Vector to invariant(Vector) So, we have three different ways of declaring constants that are not only incompatible with each other, but also incompatible with non-constants. (This is what prompted me to post the "orthogonal const proposal") In my book, there are only two kinds of plain values. Constant and variable. Why D needs 4 kinds is beyond me. Plain constant values should always be implicitly convertible to non-constants. (Plain values are values that contain no references to other data). Those examples only deal with plain values. Not a single reference is included. If this design is final and D2 can't even handle values properly I'm afraid there is not much attraction in D2 for me... There are other issues with D2 const. Some I feel are quite serious and others mainly aesthetic, but I can't be bothered to write about them now. I don't even know why I bothered writing all this down. It is not like I have any illusions about it making any difference what so ever. In the meantime, D1 works well and remains a great language that is a pleasure to use. ;) -- Oskar
Feb 01 2008
Oskar Linde:In my book, there are only two kinds of plain values. Constant and variable. Why D needs 4 kinds is beyond me.It may be beyond you (and me) but it may be a good idea anyway :-) There things in some Haskell code that I need to understand still, but I don't criticize them.I don't even know why I bothered writing all this down. It is not like I have any illusions about it making any difference what so ever.I have read this post of yours and I agree with most things you say. If lot of people think about this like you then maybe GDC will not grow all the features of D 2.x ;-)In the meantime, D1 works well and remains a great language that is a pleasure to use. ;)But it seems a dead end. I am not expert enough to offer real suggestions, but I can express a general opinion regarding the current const regime of 2.x: it may be more "correct" and well designed, but sometimes in programming "worse is better", it means that a simpler system, even if it has some known flaws, may be preferred in the long term. Trying to do the "right" thing may lead to more problems than choosing a simpler way. That's probably why C is used today too, despite its load of faults and deficiencies. This means that maybe a simpler constant system maybe be "better" even if it's "worse". Bye, bearophile
Feb 01 2008
Oskar Linde wrote:I can't help but agree. The very first thing I tried when D 2.009 was released was: struct Vector { int x,y; } invariant Vector up = {0,1}; void main() { Vector y = up; } which didn't work because: Error: no property 'opCall' for type 'Vector' ??? No idea what that means. Is it trying to implicitly call a non-existant struct static opCall "constructor"?The issue here is assigning a mutable instance of Vector to an invariant. If Vector contained a pointer, then there's a big correctness hole. So then the issue is "since Vector does not contain any pointers, shouldn't this work?" The problem then is that if one builds code that uses Vector in this manner, and later the maintainer adds a pointer member variable to Vector, then all those uses of Vector break. The reason for that particular error message is that because invariant(Vector) is not the same type as Vector, the compiler looks for an overload of the form: static invariant(Vector) opCall(Vector);and changing the main body to: Vector y; y = up; Yields: Error: cannot implicitly convert expression (up) of type invariant(Vector) to VectorThis, of course, is the reverse problem. If Vector contains a pointer, then copying an invariant pointer to a mutable pointer is a giant hole in correctness.Declaring constants using the "const" keyword is of course(?) wrong, and: (I use the function call to illustrate the point, as just initializing a variable gives the weird opCall error) const Vector c = {1,1}; void foo(invariant Vector v) {} void main() { foo(c); } Yields: Error: cannot implicitly convert expression (c) of type const(Vector) to invariant(Vector)const cannot be implicitly converted to either mutable or invariant, for the same reasons outlined above.And changing that to a "manifest constant": enum Vector c = {1,1}; Yields: Error: cannot implicitly convert expression (c) of type Vector to invariant(Vector) So, we have three different ways of declaring constants that are not only incompatible with each other, but also incompatible with non-constants. (This is what prompted me to post the "orthogonal const proposal") In my book, there are only two kinds of plain values. Constant and variable. Why D needs 4 kinds is beyond me. Plain constant values should always be implicitly convertible to non-constants. (Plain values are values that contain no references to other data).This is really the root issue here - should structs with references behave differently that structs without?Those examples only deal with plain values. Not a single reference is included. If this design is final and D2 can't even handle values properly I'm afraid there is not much attraction in D2 for me... There are other issues with D2 const. Some I feel are quite serious and others mainly aesthetic, but I can't be bothered to write about them now. I don't even know why I bothered writing all this down. It is not like I have any illusions about it making any difference what so ever. In the meantime, D1 works well and remains a great language that is a pleasure to use. ;)
Feb 02 2008
"Walter Bright" wroteOskar Linde wrote:I just wanted to respond to this one point. I think it is unreasonable for the compiler to restrict code from compiling because someone might change an API or implementation in the future. It is always possible to break code in the future by changing an API or type. Why should a developer who is certain that the type will never contain a pointer be punished because the compiler can't predict what he will do with the type in the future? What if someone does add a pointer to vector, then other pieces of code that expect copying a vector to make a 'deep copy' will break. I understand the concern, but I just don't think your argument is valid. Also, having this restriction makes it impossible to mimic builtin types with struct wrappers, as builtin types already enjoy this benefit. The real question is, is it possible for the compiler to determine whether a type contains pointers, and if so, restrict copying const or invariant versions of the type to mutable types. I believe the answer is yes, although the compiler doesn't take that into account today. I even think that it is strictly a compile-time check and therefore should not affect any existing code. If this is the case, the next question is, why would it be undesirable to have this behavior? I have not yet seen a good answer to this. -SteveI can't help but agree. The very first thing I tried when D 2.009 was released was: struct Vector { int x,y; } invariant Vector up = {0,1}; void main() { Vector y = up; } which didn't work because: Error: no property 'opCall' for type 'Vector' ??? No idea what that means. Is it trying to implicitly call a non-existant struct static opCall "constructor"?The issue here is assigning a mutable instance of Vector to an invariant. If Vector contained a pointer, then there's a big correctness hole. So then the issue is "since Vector does not contain any pointers, shouldn't this work?" The problem then is that if one builds code that uses Vector in this manner, and later the maintainer adds a pointer member variable to Vector, then all those uses of Vector break.
Feb 04 2008
Steven Schveighoffer wrote:I think it is unreasonable for the compiler to restrict code from compiling because someone might change an API or implementation in the future. It is always possible to break code in the future by changing an API or type. Why should a developer who is certain that the type will never contain a pointer be punished because the compiler can't predict what he will do with the type in the future? What if someone does add a pointer to vector, then other pieces of code that expect copying a vector to make a 'deep copy' will break. I understand the concern, but I just don't think your argument is valid. Also, having this restriction makes it impossible to mimic builtin types with struct wrappers, as builtin types already enjoy this benefit. The real question is, is it possible for the compiler to determine whether a type contains pointers, and if so, restrict copying const or invariant versions of the type to mutable types. I believe the answer is yes, although the compiler doesn't take that into account today. I even think that it is strictly a compile-time check and therefore should not affect any existing code. If this is the case, the next question is, why would it be undesirable to have this behavior? I have not yet seen a good answer to this.Your argument is sound, and I agree with it. I think it's pretty clear this will have to be changed.
Feb 05 2008
"Kris" wroteSteve - some D2 questions for ya: no nesting/chaining of results permitted? That would be a problem :)This would compile. Remember, mutable arrays are implicitly castable to const arrays :)I find the current const approach to exhibit various opposing tensions, so I'm not exactly a 'fan' at this time. Taking an example related to the discussion, and if I understand correctly, the variations on Unique!(T) are little more than attempts to hide a cast() operation under a sugary coating, right? A need for such sugar in the language syntax would surely tend to indicate a notable flaw?I believe the current approach is workable save 2 things: 1. There should be a way to have head-const classes, where the reference itself is mutable, but the class data is not 2. There should be a way to specify that a return value is mutable, but can be implicitly cast to an invariant (i.e. the unique(X) syntax Janice proposed).As far as the example api-style goes, here's hoping that this will work correctly in D2: where 'in' indicates only that the input will not be modified by the callee. By doing so, the following should be clean: Given that the input cannot be modified, you should also be able to pass a const or invariant string as an 'in' parameter; right?'in' isn't necessary in my mind. How can the callee modify the string if the callee is in the same thread? I think what you want here is const(char)[] input. This specifies that the input is not supposed to change.If that's the case, then ok. Assuming all is kosher so far, sliced 'in' arguments should also be accepted in the same fashion:if you replace in with const, then yes.right? Additionally, the optional output should always be good for use with slices also: yes?Well, as long as other doesn't allocate more data if buf can't hold it :) Then yes, it is fine. -Steve
Feb 01 2008
Steven Schveighoffer Wrote:1. There should be a way to have head-const classes, where the reference itself is mutable, but the class data is notI believe that's tail const, not head const.
Feb 01 2008
"Jason House" wroteSteven Schveighoffer Wrote:Yes of course, you are correct, my bad :) -Steve1. There should be a way to have head-const classes, where the reference itself is mutable, but the class data is notI believe that's tail const, not head const.
Feb 01 2008
"Steven Schveighoffer" <schveiguy yahoo.com> wrote ...I believe the current approach is workable save 2 things: 1. There should be a way to have head-const classes, where the reference itself is mutable, but the class data is notCheck2. There should be a way to specify that a return value is mutable, but can be implicitly cast to an invariant (i.e. the unique(X) syntax Janice proposed).If implicit, as you say, the sugar would be redundant. On the other hand, explicit casting all over the place would be a major step backwardsonly because 'in' actually appears to do what it implies, whilst apparently accepting mutable, const, or invariant. Not to mention it is a darn sight more readable than the decorative alternatives. BTW: how would one pass an invariant string as a const(char[]) argument, per the above example? Isn't there a cast required? Also, who said anything about being in the same thread? All I'm concerned with here is the semantics of the callee, and what can be passed to it without casts all over the placeAs far as the example api-style goes, here's hoping that this will work correctly in D2: where 'in' indicates only that the input will not be modified by the callee. By doing so, the following should be clean: Given that the input cannot be modified, you should also be able to pass a const or invariant string as an 'in' parameter; right?'in' isn't necessary in my mind. How can the callee modify the string if the callee is in the same thread? I think what you want here is const(char)[] input. This specifies that the input is not supposed to change.Are you quite certain of that?If that's the case, then ok. Assuming all is kosher so far, sliced 'in' arguments should also be accepted in the same fashion:if you replace in with const, then yes.the discrete type for slices in D2 does not interfere with this?right? Additionally, the optional output should always be good for use with slices also: yes?Well, as long as other doesn't allocate more data if buf can't hold it :) Then yes, it is fine.
Feb 01 2008
Kris Wrote:"Steven Schveighoffer" <schveiguy yahoo.com> wrote ...Um... by modifying it ? If it's not either 'in' or 'const' ?As far as the example api-style goes, here's hoping that this will work correctly in D2: where 'in' indicates only that the input will not be modified by the callee. By doing so, the following should be clean: Given that the input cannot be modified, you should also be able to pass a const or invariant string as an 'in' parameter; right?'in' isn't necessary in my mind. How can the callee modify the string if the callee is in the same thread?Anything casts to 'const' implicitly because 'const' doesn't violate any contracts for other types. The 'in' in D2 seems to be just an alias for 'const'.I think what you want here is const(char)[] input. This specifies that the input is not supposed to change.only because 'in' actually appears to do what it implies, whilst apparently accepting mutable, const, or invariant. Not to mention it is a darn sight more readable than the decorative alternatives. BTW: how would one pass an invariant string as a const(char[]) argument, per the above example? Isn't there a cast required?'const' and 'in' work exactly the same, just checked. void foo(in char[] s) { writeln(s); } void bar(const char[] s) { writeln(s); } foo("test"); foo("test"[0..2]); foo("test".dup); bar("test"); bar("test"[0..2]); bar("test".dup); compiles and works correctly. SnakEAre you quite certain of that?If that's the case, then ok. Assuming all is kosher so far, sliced 'in' arguments should also be accepted in the same fashion:if you replace in with const, then yes.
Feb 01 2008
Sergey Gromov Wrote:'const' and 'in' work exactly the same, just checked. void foo(in char[] s) { writeln(s); } void bar(const char[] s) { writeln(s); } foo("test"); foo("test"[0..2]); foo("test".dup); bar("test"); bar("test"[0..2]); bar("test".dup); compiles and works correctly. SnakEThank you, SnakE, for clarifying that. I'd been harbouring a misguided impression that invariant would not implicitly cast to const
Feb 01 2008
Steven Schveighoffer wrote:"Kris" wroteDoesn't "in char[]" map to "const char[]" in 2.0?As far as the example api-style goes, here's hoping that this will work correctly in D2: where 'in' indicates only that the input will not be modified by the callee. By doing so, the following should be clean: Given that the input cannot be modified, you should also be able to pass a const or invariant string as an 'in' parameter; right?'in' isn't necessary in my mind. How can the callee modify the string if the callee is in the same thread? I think what you want here is const(char)[] input. This specifies that the input is not supposed to change.This is one area where I'd like to have head const, as I think it would eliminate the problems that the "T[new]" change was proposed to address. buf[0..8] can't be resized (in place), but its contents should be modifiable. SeanAdditionally, the optional output should always be good for use with slices also: yes?Well, as long as other doesn't allocate more data if buf can't hold it :) Then yes, it is fine.
Feb 01 2008
Kris Wrote:A need for such sugar in the language syntax would surely tend to indicate a notable flaw?Yes you're right. It's an attempt to avoid undefined behavior in specification in situations where the behavior is essentially defined. 1. Anything you 'new' is basically unique: new char[] "a".dup "a" ~ "b" are all of type unique(unique(char)[]), so as any immediate literal: typeof(4) == unique(int) 2. Any non-unique type within a type casts away uniqueness: typeof(["a", "b"]) == invariant(char)[][] int[] x = [1,2,3]; int[][] y = [x]; typeof(y.dup) == int[][] but typeof([[1,2,3]]) == unique(unique(unique(int)[])[]) 3. unique(...) casts to invariant(...), const(...), or just ... implicitly with well-defined behavior. 4. There is no such thing as unique variable. Did I miss something ? SnakE
Feb 01 2008
Sergey Gromov Wrote:Kris Wrote:Of course: 5. Only unique() casts implicitly to unique(). 6. A function can have a unique() result, transitive. SnakEA need for such sugar in the language syntax would surely tend to indicate a notable flaw?Yes you're right. It's an attempt to avoid undefined behavior in specification in situations where the behavior is essentially defined. 1. Anything you 'new' is basically unique: new char[] "a".dup "a" ~ "b" are all of type unique(unique(char)[]), so as any immediate literal: typeof(4) == unique(int) 2. Any non-unique type within a type casts away uniqueness: typeof(["a", "b"]) == invariant(char)[][] int[] x = [1,2,3]; int[][] y = [x]; typeof(y.dup) == int[][] but typeof([[1,2,3]]) == unique(unique(unique(int)[])[]) 3. unique(...) casts to invariant(...), const(...), or just ... implicitly with well-defined behavior. 4. There is no such thing as unique variable. Did I miss something ?
Feb 01 2008
On 2/1/08, Sergey Gromov <snake.scaly gmail.com> wrote:1. Anything you 'new' is basically unique: 2. Any non-unique type within a type casts away uniqueness: 3. unique(...) casts to invariant(...), const(...), or just ... implicitly with well-defined behavior. 4. There is no such thing as unique variable. Did I miss something ?Only function returns, I think. Anything you "new" is unique, and unique things can also be obtained as function returns (if the function created it with "new" etc). As soon as you assign it to a variable, or use it in an expression it stops being unique. You need a way of expressing that functions can accept unique objects, and return them. e.g. unique(char)[] fillWithSpaces(unique(char)s) { foreach(ref c;s) c = ' '; return s; } We know that s[0] = 'x' has no side effects, because the function has been promised that there are no other references to s (not even in the calling code). We similarly promise to the callee that there are no other references to the return value. Then you get to call it like: string s = fillWithSpaces(new char[10]); so, new creates a unique(char)[]. That gets passed to the function. The function implicitly casts it to char[], then fills it with spaces. Then it returns it, and the function return type converts it back to unique(char)[]. Finally, it's assigned to a variable of type invariant(char)[]. There was plenty of casting going on there, but all of it was implicit
Feb 01 2008
There are some research papers about unique as a type qualifier, and it was toyed around with for a while with D. The trouble with it was: 1) complexity (yet another type qualifier) 2) it still never was quite able to get rid of all the interesting cases where a cast(invariant) (i.e. at some point trust the user) was needed 3) it was hard to understand 4) its utility seemed very limited
Feb 02 2008
On 2/2/08, Walter Bright <newshound1 digitalmars.com> wrote:There are some research papers about unique as a type qualifier, and it was toyed around with for a while with D. The trouble with it was: 1) complexity (yet another type qualifier) 2) it still never was quite able to get rid of all the interesting cases where a cast(invariant) (i.e. at some point trust the user) was needed 3) it was hard to understand 4) its utility seemed very limitedYep, you're spot on the nail. Nobody can argue with any of those points (except possibly (1), but we can go into that elsewhere if needed). Still - nobody here was suggesting unique "on a whim". Rather, it was to solve a class of problems, the most obvious of which is to allow the following to compile: string s; char[] t; char[] u = "hello" ~ s ~ t; It could well be that we don't need unique, but you are always going to get people complaining that they have to do: string s; char[] t; char[] u = "hello".dup ~ s.dup ~ t; or string s; char[] t; char[] u = cast(char[])("hello" ~ s ~ cast(invariant(char)[])t); The former doesn't look too bad but has unnecessary casts; the latter looks ugly and is error-prone. It's not just concatenation. There's a whole class of similar functions which are likely to cause similar headaches, and cause people to be critical of the whole const system. Without unique, you're going to have to convince people that superfluous dups are really OK. Maybe that's the way to go. Maybe making new and gc collection really, really fast might do that?
Feb 02 2008
On Sat, 2 Feb 2008 11:42:57 +0000, Janice Caron wrote:Still - nobody here was suggesting unique "on a whim". Rather, it was to solve a class of problems, the most obvious of which is to allow the following to compile: string s; char[] t; char[] u = "hello" ~ s ~ t;But that already compiles and runs okay.It could well be that we don't need unique, but you are always going to get people complaining that they have to do: string s; char[] t; char[] u = "hello".dup ~ s.dup ~ t;No, you don't have to do that.or string s; char[] t; char[] u = cast(char[])("hello" ~ s ~ cast(invariant(char)[])t);Now you're just being silly. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Feb 02 2008
On 2/2/08, Derek Parnell <derek psych.ward> wrote:On Sat, 2 Feb 2008 11:42:57 +0000, Janice Caron wrote:No it doesn't. The error message is: Error: non-constant expression cast(const(char)[])("hello" ~ s) ~ cast(const(char)[])t; (Perhaps you were thinking of D1.x. This is a D2.x conversation)Still - nobody here was suggesting unique "on a whim". Rather, it was to solve a class of problems, the most obvious of which is to allow the following to compile: string s; char[] t; char[] u = "hello" ~ s ~ t;But that already compiles and runs okay.
Feb 02 2008
On Sat, 2 Feb 2008 12:54:54 +0000, Janice Caron wrote:On 2/2/08, Derek Parnell <derek psych.ward> wrote:Ok, so maybe I'm /am/ going mad ... but this is what happens on my machine ... c:\temp>type test.d import std.stdio; void main() { string s; char[] t; char[] u = "hello" ~ s ~ t; writefln("%s", u); } c:\temp>dmd test c:\dmd\dmd\bin\..\..\dm\bin\link.exe test,,,user32+kernel32/noi; c:\temp>test hello c:\temp>dmd Digital Mars D Compiler v2.010 Copyright (c) 1999-2008 by Digital Mars written by Walter Bright Documentation: http://www.digitalmars.com/d/index.html -- Derek Parnell Melbourne, Australia skype: derek.j.parnellOn Sat, 2 Feb 2008 11:42:57 +0000, Janice Caron wrote:No it doesn't. The error message is: Error: non-constant expression cast(const(char)[])("hello" ~ s) ~ cast(const(char)[])t; (Perhaps you were thinking of D1.x. This is a D2.x conversation)Still - nobody here was suggesting unique "on a whim". Rather, it was to solve a class of problems, the most obvious of which is to allow the following to compile: string s; char[] t; char[] u = "hello" ~ s ~ t;But that already compiles and runs okay.
Feb 02 2008
On 2/2/08, Derek Parnell <derek psych.ward> wrote:void main() { string s; char[] t; char[] u = "hello" ~ s ~ t; writefln("%s", u); } c:\temp>dmd test c:\dmd\dmd\bin\..\..\dm\bin\link.exe test,,,user32+kernel32/noi; c:\temp>test helloBah! Dangnabbit! OK then - /this/ doesn't compile:string s; char[] t; string u = "hello" ~ s ~ t;I'm fairly sure that the char[] version didn't compile in some earlier version of D, but I'm not going to redownload the old versions to find out. In any case the string version doesn't compile, and that's just as bad. The error message indicates that the compiler can't convert a char[] to an invariant(char)[], which means that the type of the RHS is now deemed to be char[]. (In previous releases, I'm sure it would have been const(char)[], but I see nothing in the change log to account for it. Maybe it's I who's going mad?)
Feb 02 2008
On Sun, 3 Feb 2008 07:44:03 +0000, Janice Caron wrote:On 2/2/08, Derek Parnell <derek psych.ward> wrote:Of course it doesn't. As far as I can recall, D has always said that concatenation creates a dynamic array, and since 'const' has been in use, that array is also a mutable one.void main() { string s; char[] t; char[] u = "hello" ~ s ~ t; writefln("%s", u); } c:\temp>dmd test c:\dmd\dmd\bin\..\..\dm\bin\link.exe test,,,user32+kernel32/noi; c:\temp>test helloBah! Dangnabbit! OK then - /this/ doesn't compile:string s; char[] t; string u = "hello" ~ s ~ t;... the string version doesn't compile, and that's just as bad. The error message indicates that the compiler can't convert a char[] to an invariant(char)[], which means that the type of the RHS is now deemed to be char[].Well its not so much that it can't convert it, but more that it won't convert it implicitly; you have to be explicit. Try this instead ... string s; char[] t; string u = ("hello" ~ s ~ t).idup; We have to be aware that the rule in D is that RHS concatentation always produces a mutable dynamic array, so if we want it to be immutable we need to be explicit about it. Maybe smart compilers can even avoid redundant copying if they see the ".idup" operation in this context.(In previous releases, I'm sure it would have been const(char)[], but I see nothing in the change log to account for it. Maybe it's I who's going mad?)I don't think it (the RHS formed by concatenation) was ever a const thingy. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Feb 03 2008
On 2/3/08, Derek Parnell <derek psych.ward> wrote:As far as I can recall, D has always said that concatenation creates a dynamic array, and since 'const' has been in use, that array is also a mutable one.I don't think that's true. Back in November, Steven Schveighoffer wrote this post: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=61276 Stephen was finding he needed to explicitly .dup to get a mutable result. Since nothing implicitly casts to both char[] and string, some duping or iduping or explicit casting would always be necessary in some circumstance or other. You can't keep /everyone/ happy (as you could in D1). Unique would have fixed that problem. But that said, Walter has now effectively closed this discussion by saying that concatenation will in future give a polysemous result - a hack which does the same as what unique would have done, but /only/ in that circumstance. Once this is done, everyone's going to be happy.
Feb 03 2008
On Sun, 3 Feb 2008 08:43:54 +0000, Janice Caron wrote:On 2/3/08, Derek Parnell <derek psych.ward> wrote:With respect and meaning no offense, however even though that post of Steven's says...As far as I can recall, D has always said that concatenation creates a dynamic array, and since 'const' has been in use, that array is also a mutable one.I don't think that's true. Back in November, Steven Schveighoffer wrote this post: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=61276 Stephen was finding he needed to explicitly .dup to get a mutable result.He has not shown any evidence that that is actually the case. It could be that he is assuming that to be the case, because each attempt I have made to duplicate his assertion has just shown me that the compiler regards 'a2' as mutable. The current D2 compiler treats 'a2' as mutable. -- Derek Parnell Melbourne, Australia skype: derek.j.parnellauto a2 = a1 ~ "\n"; Now, a2 is declared as a const(char)[] array,
Feb 03 2008
"Derek Parnell" wroteOn Sun, 3 Feb 2008 08:43:54 +0000, Janice Caron wrote:I probably should respond to this :) D2 at the time did have an issue with a similar statement. I don't know if that's the case now, but it was the basis for my bug report (1654). What triggered me to write the post is that at the time I was attempting to port tango to D 2 (I think it was 2.006 or 2.007), and there was a piece of a function that attempted to add a string literal to the end of a const or mutable char array (can't remember which), and since 2.x has strings as invariant, the compiler was complaining. I was forced to do a dup, or needlessly initialize a dynamic array. I may have misrepresented the exact problem, which may have lead to me posting valid code, sorry about that. I thought I had posted the correct thing. I believe that the return value of any concatenation operation should be mutable, as it always creates a new copy. The issue becomes when you are dealing with only invariants, i.e.: string s = "hello" ~ " world"; Now, if the concatenation operation returned a mutable string, then you need an explicit cast. However I think the problem is being worked on by Walter and Andrei. -SteveOn 2/3/08, Derek Parnell wrote:With respect and meaning no offense, however even though that post of Steven's says...As far as I can recall, D has always said that concatenation creates a dynamic array, and since 'const' has been in use, that array is also a mutable one.I don't think that's true. Back in November, Steven Schveighoffer wrote this post: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=61276 Stephen was finding he needed to explicitly .dup to get a mutable result.He has not shown any evidence that that is actually the case. It could be that he is assuming that to be the case, because each attempt I have made to duplicate his assertion has just shown me that the compiler regards 'a2' as mutable. The current D2 compiler treats 'a2' as mutable.auto a2 = a1 ~ "\n"; Now, a2 is declared as a const(char)[] array,
Feb 03 2008
"Steven Schveighoffer" wrote"Derek Parnell" wroteI just tested this out in D 2.006, and you are correct. a1 ~ "\n" results in a char[]. I think when I was porting the code, it looked more like: a1 ~ '\n'; which results in a const(char)[]. So the example I gave originally is invalid, but the point is not. The result of concatenation should be determined by the usage of the result, and allowed if it doesn't break constness. -SteveOn Sun, 3 Feb 2008 08:43:54 +0000, Janice Caron wrote:I probably should respond to this :) D2 at the time did have an issue with a similar statement. I don't know if that's the case now, but it was the basis for my bug report (1654). What triggered me to write the post is that at the time I was attempting to port tango to D 2 (I think it was 2.006 or 2.007), and there was a piece of a function that attempted to add a string literal to the end of a const or mutable char array (can't remember which), and since 2.x has strings as invariant, the compiler was complaining. I was forced to do a dup, or needlessly initialize a dynamic array. I may have misrepresented the exact problem, which may have lead to me posting valid code, sorry about that. I thought I had posted the correct thing. I believe that the return value of any concatenation operation should be mutable, as it always creates a new copy. The issue becomes when you are dealing with only invariants, i.e.: string s = "hello" ~ " world"; Now, if the concatenation operation returned a mutable string, then you need an explicit cast. However I think the problem is being worked on by Walter and Andrei. -SteveOn 2/3/08, Derek Parnell wrote:With respect and meaning no offense, however even though that post of Steven's says...As far as I can recall, D has always said that concatenation creates a dynamic array, and since 'const' has been in use, that array is also a mutable one.I don't think that's true. Back in November, Steven Schveighoffer wrote this post: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=61276 Stephen was finding he needed to explicitly .dup to get a mutable result.He has not shown any evidence that that is actually the case. It could be that he is assuming that to be the case, because each attempt I have made to duplicate his assertion has just shown me that the compiler regards 'a2' as mutable. The current D2 compiler treats 'a2' as mutable.auto a2 = a1 ~ "\n"; Now, a2 is declared as a const(char)[] array,
Feb 04 2008
On Mon, 4 Feb 2008 10:01:06 -0500, Steven Schveighoffer wrote:I just tested this out in D 2.006, and you are correct. a1 ~ "\n" results in a char[]. I think when I was porting the code, it looked more like: a1 ~ '\n'; which results in a const(char)[]. So the example I gave originally is invalid, but the point is not. The result of concatenation should be determined by the usage of the result, and allowed if it doesn't break constness.Yes, and another point is why does the compiler treat string ~ char and string ~ string differently!? -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Feb 04 2008
Janice Caron wrote:Still - nobody here was suggesting unique "on a whim". Rather, it was to solve a class of problems, the most obvious of which is to allow the following to compile: string s; char[] t; char[] u = "hello" ~ s ~ t;I understand that's a problem. The solution, however, doesn't need unique. It can be solved by having the result type of ~ be a polysemous type, i.e. its type is based on how it is used.
Feb 02 2008
On 2/2/08, Walter Bright <newshound1 digitalmars.com> wrote:It can be solved by having the result type of ~ be a polysemous type, i.e. its type is based on how it is used.Nice! (I haven't quite wrapped my head around those, yet!)
Feb 02 2008
On 2/2/08, Walter Bright <newshound1 digitalmars.com> wrote:It can be solved by having the result type of ~ be a polysemous type, i.e. its type is based on how it is used.Will we be able to write our own functions which are declared to return polysemous types? Sorry, I'm getting all excited now! :-)
Feb 02 2008
Janice Caron wrote:On 2/2/08, Walter Bright <newshound1 digitalmars.com> wrote:No. They're basically a hack.It can be solved by having the result type of ~ be a polysemous type, i.e. its type is based on how it is used.Will we be able to write our own functions which are declared to return polysemous types?
Feb 02 2008
Walter Bright wrote:Janice Caron wrote:We can do it with macros, though, right?On 2/2/08, Walter Bright <newshound1 digitalmars.com> wrote:No. They're basically a hack.It can be solved by having the result type of ~ be a polysemous type, i.e. its type is based on how it is used.Will we be able to write our own functions which are declared to return polysemous types?
Feb 03 2008
Robert Fraser wrote:Walter Bright wrote:Yes.Janice Caron wrote:We can do it with macros, though, right?On 2/2/08, Walter Bright <newshound1 digitalmars.com> wrote:No. They're basically a hack.It can be solved by having the result type of ~ be a polysemous type, i.e. its type is based on how it is used.Will we be able to write our own functions which are declared to return polysemous types?
Feb 03 2008
If we had a opImplicitCast (and it was overloadable), then it would be pretty easy to implement a template with thunks: template PolysemousContainer(A,B) { A delegate() thunk1; B delegate() thunk2; A opImplicitCast() { return thunk1(); } B opImplicitCast() { return thunk2(); } } PolySemousContainer(A,B) Polysemous(A,B)(A delegate() thunk1, B delegate() thunk2) { PolysemousContainer!(A,B) ret; ret.thunk1 = thunk1; ret.thunk2 = thunk2; return ret; } Walter Bright wrote:Janice Caron wrote:On 2/2/08, Walter Bright <newshound1 digitalmars.com> wrote:No. They're basically a hack.It can be solved by having the result type of ~ be a polysemous type, i.e. its type is based on how it is used.Will we be able to write our own functions which are declared to return polysemous types?
Feb 03 2008
On 03/02/2008, Russell Lewis <webmaster villagersonline.com> wrote:If we had a opImplicitCast (and it was overloadable), then it would be pretty easy to implement a template with thunks:It would - but you'd quickly find ambiguities, at which the compiler would also complain, again requiring an explicit cast to keep the compiler happy.
Feb 03 2008
Janice Caron wrote:On 03/02/2008, Russell Lewis <webmaster villagersonline.com> wrote:Can you expand on this? Another (less elegant) solution to this would be to allow overloaded opCast(). You could, of course, just access the underlying fields in the struct, but that seems less than desirable. I could imagine an "OptionalInvariant" template which held two thunks, one of which would produce the invariant version, and one which would produce the variant version.If we had a opImplicitCast (and it was overloadable), then it would be pretty easy to implement a template with thunks:It would - but you'd quickly find ambiguities, at which the compiler would also complain, again requiring an explicit cast to keep the compiler happy.
Feb 04 2008
On 04/02/2008, Russell Lewis <webmaster villagersonline.com> wrote:Janice Caron wrote:Sure. If one function, f, returned PolysemousContainer(A,B), and another function, g, was overloaded thus: g(A a) { /* do something */ } g(B b) { /* do something else */ } then the expression g( f( ) ) would be ambiguous.On 03/02/2008, Russell Lewis <webmaster villagersonline.com> wrote:Can you expand on this?If we had a opImplicitCast (and it was overloadable), then it would be pretty easy to implement a template with thunks:It would - but you'd quickly find ambiguities, at which the compiler would also complain, again requiring an explicit cast to keep the compiler happy.
Feb 05 2008
Janice Caron wrote:On 04/02/2008, Russell Lewis <webmaster villagersonline.com> wrote:Ok, good point. But, IMHO, that isn't a good argument against polysemous types. Remember that we already have that problem with various built-in types which have multiple possible implicit casts (such as numeric types). IMHO, the problem is not a problem with the polysemous type, but with the *user* of a polysemous type, and in that case, it is quite reasonable to require the user to resolve the ambiguity. For users which don't have any ambiguity, polysemous types express a clear and very useful thing: "There are two or more perfectly valid ways to express this value. Which do you want?"Janice Caron wrote:Sure. If one function, f, returned PolysemousContainer(A,B), and another function, g, was overloaded thus: g(A a) { /* do something */ } g(B b) { /* do something else */ } then the expression g( f( ) ) would be ambiguous.On 03/02/2008, Russell Lewis <webmaster villagersonline.com> wrote:Can you expand on this?If we had a opImplicitCast (and it was overloadable), then it would be pretty easy to implement a template with thunks:It would - but you'd quickly find ambiguities, at which the compiler would also complain, again requiring an explicit cast to keep the compiler happy.
Feb 05 2008
Walter Bright wrote:Janice Caron wrote:Awesome. I was wondering if that's where we were going with this. SeanStill - nobody here was suggesting unique "on a whim". Rather, it was to solve a class of problems, the most obvious of which is to allow the following to compile: string s; char[] t; char[] u = "hello" ~ s ~ t;I understand that's a problem. The solution, however, doesn't need unique. It can be solved by having the result type of ~ be a polysemous type, i.e. its type is based on how it is used.
Feb 02 2008
Walter Bright wrote:Janice Caron wrote:More simply, catenation of a mutable variable with a non-mutable one could yield a mutable one by default. A const cast is safer than a non-const cast, after all. One could extend that to all binary operators, as well. This might reduce the usage of const. Walter doesn't seem concerned about that, since he ignored just about everyone here when we asked for function arguments to be const by default.Still - nobody here was suggesting unique "on a whim". Rather, it was to solve a class of problems, the most obvious of which is to allow the following to compile: string s; char[] t; char[] u = "hello" ~ s ~ t;I understand that's a problem. The solution, however, doesn't need unique. It can be solved by having the result type of ~ be a polysemous type, i.e. its type is based on how it is used.
Feb 03 2008
On 03/02/2008, Christopher Wright <dhasenan gmail.com> wrote:More simply, catenation of a mutable variable with a non-mutable one could yield a mutable one by default. A const cast is safer than a non-const cast, after all.Except that "non-mutable" doesn't necessarily mean const, and "non-const" doesn't necessarily mean mutable. I think you left invariant out of the mix there.This might reduce the usage of const. Walter doesn't seem concerned about that, since he ignored just about everyone here when we asked for function arguments to be const by default.I wouldn't say no to revisiting that possibility! :-)
Feb 03 2008
Janice Caron wrote:On 03/02/2008, Christopher Wright <dhasenan gmail.com> wrote:My suggestion was: invariant(char)[] ~ char[] yields char[] const(char)[] ~ char[] yields char[] For clarity: invariant(char)[] ~ const(char)[] yields invariant(char)[]More simply, catenation of a mutable variable with a non-mutable one could yield a mutable one by default. A const cast is safer than a non-const cast, after all.Except that "non-mutable" doesn't necessarily mean const, and "non-const" doesn't necessarily mean mutable. I think you left invariant out of the mix there.
Feb 03 2008
On 2/2/08, Janice Caron <caron800 googlemail.com> wrote:The former doesn't look too bad but has unnecessary castsI meant, unnecessary dups.
Feb 02 2008
On 2/2/08, Janice Caron <caron800 googlemail.com> wrote:Still - nobody here was suggesting unique "on a whim". Rather, it was to solve a class of problems, the most obvious of which is to allow the following to compile: string s; char[] t; char[] u = "hello" ~ s ~ t;That said, we could always have a concatenation function in phobos: string s; char[] t; char[] u = cat("hello",s,t);
Feb 02 2008
Janice Caron wrote:On 2/2/08, Janice Caron <caron800 googlemail.com> wrote:Which is silly, when we have a catenation operator in the language.Still - nobody here was suggesting unique "on a whim". Rather, it was to solve a class of problems, the most obvious of which is to allow the following to compile: string s; char[] t; char[] u = "hello" ~ s ~ t;That said, we could always have a concatenation function in phobos: string s; char[] t; char[] u = cat("hello",s,t);
Feb 02 2008
On 2/2/08, Christopher Wright <dhasenan gmail.com> wrote:Janice Caron wrote:Not really. We could have two functions, cat and icat, whereby cat always returns a mutable, and icat always returns an invariant. The concatenation operator, by contrast, always returns the same type as its inputs.That said, we could always have a concatenation function in phobos: string s; char[] t; char[] u = cat("hello",s,t);Which is silly, when we have a catenation operator in the language.
Feb 02 2008
Janice Caron wrote:Not really. We could have two functions, cat and icat, whereby cat always returns a mutable, and icat always returns an invariant. The concatenation operator, by contrast, always returns the same type as its inputs.The issue with the concatenation operator is that its operands need not be the same type. You can use invariant(char)[] and char[], and invariant(char)[] takes precedence. I'm not sure how often this issue comes up. If you have a buffer, you can append to it with ~=. You might want to prepend something, though, and in that case, the current behavior will be problematic. If you want to prepend something to a string, but you want to build the stuff you prepend...this seems like a marginal use case, more so than the buffer use case. It's easy enough to do: char[] build; const(char)[] prepend = build; str = prepend ~ build; The workaround for a mutable buffer is: char[] tmpBuffer = new char[buffer.length + prepend.length]; tmpBuffer[0..prepend.length] = prepend[]; tmpBuffer[prepend.length..$] = buffer[]; buffer = tmpBuffer; That is extremely ugly.
Feb 03 2008
On Feb 1, 2008 3:15 AM, Steven Schveighoffer <schveiguy yahoo.com> wrote:How is this not what you want?In D1, it's /exactly/ what you want. But if you've ever tried to port Tango code to D2 (I have) then this is the point at which constancy gets in the way. Assuming one wants const correctness at higher levels, the compiler baulks because at some point you end up trying to pass const data to a non-const function. To get it to compile, you more or less have to take const out right the way up the chain to the higher level API, and ultimately end up not being able to pass strings (which are invariant) to functions to which you would natually want to pass them. It's the whole "rippling up" effect. From memory, I believe my workaround was to keep those nifty functions exactly as is, but hide some casting in upper layers of the API. Of course, for that not to result in undefined behavior, you have to understand /exactly/ what is being modified and when, and that's where the brain starts to hurt.
Feb 01 2008
"Janice Caron" wroteOn Feb 1, 2008 3:15 AM, Steven Schveighoffer wrote:D1 does not have this type of function signature possible, I was talking about D2.How is this not what you want?In D1, it's /exactly/ what you want.But if you've ever tried to port Tango code to D2 (I have)I have. In fact, Frank Benoit and I successfully produced a tango tree that worked with D 2.007.then this is the point at which constancy gets in the way. Assuming one wants const correctness at higher levels, the compiler baulks because at some point you end up trying to pass const data to a non-const function.We did not have that happen to us. We did not have a single place where we had to cast away const. Of course, we did not produce a fully const tango, but that was mostly because there were still some bugs with const that made it impossible to be const-correct in Tango. Namely, that you could not overload a const function with a non-const function, and that you could override a const function with a non const function )(there were probably others, but those stand out in my mind). I'm not sure if these have been fixed in the latest DMD, but I think your arguments are anecdotal without an anecdote. I believe it is possible with the proposed const scheme to have const arguments and const functions and be fully const-correct. Invariant functions and invariant args, that's a different story. Because you cannot yet implicitly cast a mutable return value to an invariant, even though technically it is, code is going to look ugly. either you have wrapper functions for everything which do the cast, or you have to cast in your code. This is where your unique idea comes in handy, and that's really the only place I support it (as the return value of a function). -Steve
Feb 01 2008
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Janice Caron wrote:Once again, this problem arises because D has no way to express uniqueness. If we had such a means, then the const-correct prototype would be unique(char)[] niftyStringFunction(unique(char)[] outBuffer, string input)Looks a lot like C’s proposed noalias to me; dmr’s problems with noalias might not apply exactly to D, but I’d be cautious. - --Joel -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFHosNqzLx4GzBL9dYRAiOgAJ93AkHzAJKfyuSA9ybFZygg8oBTtACg08x8 qjDDivlk8ex1i+YpkrOfSI0= =equv -----END PGP SIGNATURE-----
Jan 31 2008
Janice Caron Wrote:On 2/1/08, Sergey Gromov <snake.scaly gmail.com> wrote:Of course, you're right.1. Anything you 'new' is basically unique: 2. Any non-unique type within a type casts away uniqueness: 3. unique(...) casts to invariant(...), const(...), or just ... implicitly with well-defined behavior. 4. There is no such thing as unique variable. Did I miss something ?Only function returns, I think.As soon as you assign it to a variable, or use it in an expression it stops being unique.Inline array initialization is also an expression, and unique must survive to allow the whole array be unique. Why using in expression makes data not unique ?You need a way of expressing that functions can accept unique objects, and return them. e.g. unique(char)[] fillWithSpaces(unique(char)s) { foreach(ref c;s) c = ' '; return s; }I'm afraid that's not possible if you want strict type safety. A unique parameter effectively results in a local variable of type unique. Or, even worse, of non-unique type. In either case, the compiler doesn't have any chance to check what's happening and enforce type safety. Which is a must: implicit conversions must be safe. So the only way to return a unique result from a function is to dup or new or inline-construct directly within its return statement. Other return values are illegal. SnakE
Feb 01 2008
On 2/1/08, Sergey Gromov <snake.scaly gmail.com> wrote:What I'm telling the compiler here is that the function's formal parameter, s, is unique at the callee site. Within the function, it stops being unique. Thus, within the function body, s is just a char[]. At the callee site, however, the compiler gets to complain if a non-unique type is passed to the function. (My example passed it the return value from new, so it was OK).You need a way of expressing that functions can accept unique objects, and return them. e.g. unique(char)[] fillWithSpaces(unique(char)[]s) { foreach(ref c;s) c = ' '; return s; }I'm afraid that's not possible if you want strict type safetySo the only way to return a unique result from a function is to dup or new or inline-construct directly within its return statement. Other return values are illegal.Good point. It should have been return cast(unique)s;
Feb 01 2008
Janice Caron Wrote:What I'm telling the compiler here is that the function's formal parameter, s, is unique at the callee site. Within the function, it stops being unique. Thus, within the function body, s is just a char[]. At the callee site, however, the compiler gets to complain if a non-unique type is passed to the function.On second thoughts, why would you want a function with a unique parameter ? Nothing casts to unique(), so that the caller is always required to dup() or whatever. Why force the user do it if you can always dup inside the function if you wish ? I think it only complicates the function usage. SnakE(My example passed it the return value from new, so it was OK).So the only way to return a unique result from a function is to dup or new or inline-construct directly within its return statement. Other return values are illegal.Good point. It should have been return cast(unique)s;
Feb 01 2008
On 2/1/08, Sergey Gromov <snake.scaly gmail.com> wrote:On second thoughts, why would you want a function with a unique parameter ? Nothing casts to unique(), so that the caller is always required to dup() or whatever.So that you can do string s; string t = f( g( h( i( j( k( s )))))); The functions f,g,h,i,j and k all return unique. The functions f,g,h,i and j (but not k) all accept a unique parameter. In this example, no duping or casting is required, because the temporary value is unique right the way through the chain.Why force the user do it if you can always dup inside the function if you wish ?That would be five dups in the above example. The fact is, if don't mind duping, we don't need unique /at all/. e.g. string s; char[] t; char[] u = "hello".dup ~ s.dup ~ t; But if we're concerned to minimise allocations and duplications, then you need functions that /accept/ uniques, as well as functions that return them, for reasons demonstrated in the first example.I think it only complicates the function usage.As with an other function in-contracts, yes. But misuse would be compiler checkable, so string s; string t = f( s ); (where f is as above) would be a compile error (cannot implicitly cast invariant(char)[] to unique(char)[]). The caller of f would have to change it to one of: string t = f( s.dup ); or string t = f( cast(unique)s ); In the latter case, of course, it would be the caller's responsibility to guarantee uniqueness. If the programmer is wrong, undefined behaviour results.
Feb 01 2008
Janice Caron Wrote:On 2/1/08, Sergey Gromov <snake.scaly gmail.com> wrote:Something tells me that this use case is very rare, but the dangers are great: every and each of those functions will include an explicit cast to unique. Can't prove the rariness though. The discussion loses its point unfortunately. The chances that the unique() gets into the language are very slim. SnakEOn second thoughts, why would you want a function with a unique parameter ? Nothing casts to unique(), so that the caller is always required to dup() or whatever.So that you can do string s; string t = f( g( h( i( j( k( s ))))));
Feb 03 2008
"Janice Caron" wroteOn 2/1/08, Sergey Gromov wrote:Can't you just declare the functions as accepting mutable strings? Doesn't unique(char)[] implicitly cast to char[]? As Sergey says, this FORCES you to do a dup if you don't have a unique value. -SteveOn second thoughts, why would you want a function with a unique parameter ? Nothing casts to unique(), so that the caller is always required to dup() or whatever.So that you can do string s; string t = f( g( h( i( j( k( s )))))); The functions f,g,h,i,j and k all return unique. The functions f,g,h,i and j (but not k) all accept a unique parameter. In this example, no duping or casting is required, because the temporary value is unique right the way through the chain.
Feb 03 2008
Janice Caron Wrote:The possibility to cast should still be there for complex cases where you can't/don't want to put data allocation into return statement but can guarantee its uniqueness. SnakESo the only way to return a unique result from a function is to dup or new or inline-construct directly within its return statement. Other return values are illegal.Good point. It should have been return cast(unique)s;
Feb 01 2008
On 2/1/08, Sergey Gromov <snake.scaly gmail.com> wrote:The possibility to cast should still be there for complex cases where you can't/don't want to put data allocation into return statement but can guarantee its uniqueness.I disagree. For type safety, it should be required wherever a non-unique type is assigned to a unique type. By necessity, this must include function returns. Thus unique(char)[] f() { char[] s = whatever(); return cast(unique)s; } In this case, the cast to unique is unavoidable, since, if you omit it, you would be attempting to implicitly cast s (a char[]) to the return type, upon return. This cast is required for type safety, and justified because the code does guarantee uniqueness (because s goes out of scope on return, so at that point there will be no references to the data apart from the return value). It is almost impossible to /prove/ uniqueness, so it is reasonable to rely on programmer assertion (casts). What the compiler /can/ do, and do well, is ensure type safety.
Feb 01 2008
On 2/2/08, Janice Caron <caron800 googlemail.com> wrote:On 2/1/08, Sergey Gromov <snake.scaly gmail.com> wrote:Actually, I just realised I might be agreeing (not disagreeing) with you there! Sorry about that.The possibility to cast should still be there for complex cases where you can't/don't want to put data allocation into return statement but can guarantee its uniqueness.I disagree.
Feb 01 2008