digitalmars.D - I just got it! (invariant/const)
- Lionello Lunesu (18/18) Apr 09 2008 After reading Walter's posts these last weeks and Andrei's PDF, I think ...
- Jason House (4/30) Apr 09 2008 They say the best way to learn is to explain to someone else...
- Janice Caron (19/20) Apr 09 2008 I don't think that's correct.
- Jason House (10/16) Apr 09 2008 Consider this example:
- Janice Caron (5/7) Apr 09 2008 Oh well, that's obvious. f() can't call D.invMemberFunc() because
- Simen Kjaeraas (5/12) Apr 09 2008 I think it is because invMemberFunc is invariant. Many read this as
- Steven Schveighoffer (8/20) Apr 09 2008 I think there was a suggestion at one point to change const/invariant
- Jason House (5/22) Apr 09 2008 That's certainly a piece of it, but it's more than that. A lot of peopl...
- Georg Wrede (6/11) Apr 09 2008 I seriously think the problem is with the phrase "invMemberFunc is
- Jason House (2/17) Apr 09 2008 Is that really the best solution? Why not have do what people nievely t...
- Lionello Lunesu (4/22) Apr 09 2008 I agree.. And one less keyword, which, it seems, is a goal in itself.
- Georg Wrede (10/35) Apr 10 2008 I wasn't thinking of changing D, I was thinking more what we should
- Jason House (3/33) Apr 10 2008 If we have to say special qualifiers every time we talk about stuff, it'...
- Georg Wrede (56/76) Apr 09 2008 ((Upon proofreading it dawned to me that the example above may contain
- Janice Caron (18/39) Apr 09 2008 The word "invariant", in this position, means that the function's
- Georg Wrede (31/85) Apr 09 2008 Ok.
- Janice Caron (34/52) Apr 09 2008 No, again, that we my fault for misreading the question, and for which
- Jason House (2/7) Apr 09 2008 D already outlaws having code that isn't run. I would prefer for code t...
- Lionello Lunesu (15/25) Apr 09 2008 Why? If pure-ness (uh, purity?) can be checked, it can also be deducted ...
- Bill Baxter (20/38) Apr 09 2008 I was thinking along the same lines last week or so. A lot of things
- Janice Caron (3/11) Apr 09 2008 No I did not. That was Jason House. Please be careful with your level
- Lionello Lunesu (5/19) Apr 10 2008 Uh, the "..." part contains what you've said, not what follows it, so th...
- Janice Caron (13/15) Apr 09 2008 It would slow down compilation no end though. For example, a pure
- Lionello Lunesu (8/23) Apr 10 2008 :-) thanks.
- Frits van Bommel (9/29) Apr 10 2008 Visited? Yes.
- Georg Wrede (9/60) Apr 09 2008 Thanks. (Although I'd really love to see less possibilities here.
- guslay (4/7) Apr 09 2008 Shouldn't it be
- Bill Baxter (6/17) Apr 09 2008 Just a nitpick, but "pure" applies to the function as a whole. So
- Steven Schveighoffer (16/23) Apr 09 2008 No.
- Janice Caron (5/9) Apr 09 2008 Trouble is, that means we'll end up having to write "invariant" all
- guslay (3/36) Apr 09 2008 c.getX() is equivalent to " int getX( ref C this ) ", so its not pure.
- Steven Schveighoffer (10/46) Apr 09 2008 AFAIK, C.getX() is equivalent to int getX(C this).
- guslay (3/16) Apr 09 2008 I can't think of a counter-example, you might be correct.
- Janice Caron (17/20) Apr 09 2008 No, that's because of Andrei's
- Steven Schveighoffer (27/48) Apr 09 2008 Hm.. I glossed over that rule :) But by the same rule, my example class...
- Janice Caron (7/18) Apr 09 2008 Except that there's no way to initialise i.
- Steven Schveighoffer (5/26) Apr 09 2008 I think this is a limitation that should be lifted, as there may be reas...
- Janice Caron (44/57) Apr 09 2008 OK, I'm gonna risk it. :-)
-
Simen Kjaeraas
(30/48)
Apr 09 2008
On Wed, 09 Apr 2008 20:40:38 +0200, Janice Caron
- Janice Caron (2/11) Apr 09 2008 That's interesting. I did not know that.
-
Simen Kjaeraas
(7/20)
Apr 10 2008
On Thu, 10 Apr 2008 01:07:51 +0200, Janice Caron
- Janice Caron (6/12) Apr 09 2008 Hopefully, it will just be
- Janice Caron (6/11) Apr 09 2008 My apologies. Of course it's legal. I was misreading. (Although you
- Steven Schveighoffer (77/150) Apr 09 2008 It means that the 'this' pointer is invariant. We don't see the body of...
- Georg Wrede (5/19) Apr 09 2008 Before commenting on the rest,
- Steven Schveighoffer (14/33) Apr 09 2008 The whole problem comes from the parsing. I'm not a guru when it comes ...
- Georg Wrede (5/43) Apr 09 2008 Well, the localness of c is established with char[] c on the preceding
- Georg Wrede (23/170) Apr 09 2008 Ok. But I strongly resent being able to put the invariant either before
- Jason House (11/21) Apr 09 2008 [Corrected example - Thanks george]
- Denton Cockburn (7/29) Apr 09 2008 Couldn't the compiler insert the cast based on the declaration?
- Janice Caron (18/22) Apr 09 2008 No, because objects created by new are not necessarily transitively
- Steven Schveighoffer (24/47) Apr 09 2008 Sure there is :)
- Georg Wrede (3/35) Apr 09 2008 It should be illegal to cast objects invariant unless the compiler can
- Steven Schveighoffer (8/43) Apr 09 2008 This cannot be guaranteed by the compiler. It is depending on you to en...
- Georg Wrede (2/50) Apr 09 2008 My mistake.
- Robert Fraser (3/6) Apr 09 2008 And the compiler can make the decision for you as to which would be more...
After reading Walter's posts these last weeks and Andrei's PDF, I think the D 2.0 invariant/const make a lot of sense, and I can see the how it opens up new possibilities. I was a bit worried for a performance penalty because of the need for "idup"s, but then I realized that you only .idup when you use "invariant()" when you actually don't care about it being invariant or not. So converting all D 1.0 "char[]" to D 2.0 "string" might not be such a good idea. One thing that I like is that suddenly C++ "const &" construction can be standardized and made safe at the same time! Because of invariant, "in" can safely pass the arguments by ref, instead of by value. Sorry for repeating what most probably already know, but it just felt like a revelation that I had to share :-) A bit slow perhaps, but I'm pretty sure I'm not the last one to /get it/. Thanks Walter, Andrei for explaining this invariant/const thing over and over again, in different forms. Now I'll have to change Phobos 2.0 to get "foreach(string s; new BufferedFile(..))" working ;-) L.
Apr 09 2008
They say the best way to learn is to explain to someone else... Many have said FP style multithreading requires pure functions with invariant data. The strange thing is that pure functions can't call invariant member functions of their invariant data. Why would that be? That's especially strange when the justification for invariant is multithreading. I've been arguing for a while that mutable access to globals is like head const and kills most of the benefits from the const system. Please help me understand why I am wrong. Lionello Lunesu Wrote:After reading Walter's posts these last weeks and Andrei's PDF, I think the D 2.0 invariant/const make a lot of sense, and I can see the how it opens up new possibilities. I was a bit worried for a performance penalty because of the need for "idup"s, but then I realized that you only .idup when you use "invariant()" when you actually don't care about it being invariant or not. So converting all D 1.0 "char[]" to D 2.0 "string" might not be such a good idea. One thing that I like is that suddenly C++ "const &" construction can be standardized and made safe at the same time! Because of invariant, "in" can safely pass the arguments by ref, instead of by value. Sorry for repeating what most probably already know, but it just felt like a revelation that I had to share :-) A bit slow perhaps, but I'm pretty sure I'm not the last one to /get it/. Thanks Walter, Andrei for explaining this invariant/const thing over and over again, in different forms. Now I'll have to change Phobos 2.0 to get "foreach(string s; new BufferedFile(..))" working ;-) L.
Apr 09 2008
On 09/04/2008, Jason House <jason.james.house gmail.com> wrote:The strange thing is that pure functions can't call invariant member functions of their invariant data.I don't think that's correct. class C { int x; int f() invariant pure { return x; } } invariant c = cast(invariant) new C; int n = c.f(); Should work just fine. Of course, the explicit cast necessary to create an invariant C in the first place is a bit ugly. Maybe we need "inew" to make new invariant objects? Remember, a member function is nothing more nor less than a global function with a hidden parameter and some name mangling. That means that the rules for purity of member functions are identical to those of global functions. You just have to insist that "this" be invariant.
Apr 09 2008
Janice Caron Wrote:On 09/04/2008, Jason House <jason.james.house gmail.com> wrote:Consider this example: class D{ void invMemberFunc() invariant; // not pure } class C{ int f(invariant D) invariant pure{ D.invMemberFunc(); // illegal } }The strange thing is that pure functions can't call invariant member functions of their invariant data.I don't think that's correct. [example showing misinterpretation of what I'm talking about]
Apr 09 2008
On 09/04/2008, Jason House <jason.james.house gmail.com> wrote:Consider this example: <snip>Oh well, that's obvious. f() can't call D.invMemberFunc() because D.invMemberFunc() isn't pure. Pure functions can only call other pure functions. You know that. I know that. Why would anyone think it strange?
Apr 09 2008
On Wed, 09 Apr 2008 15:40:37 +0200, Janice Caron <caron800 googlemail.com> wrote:On 09/04/2008, Jason House <jason.james.house gmail.com> wrote:I think it is because invMemberFunc is invariant. Many read this as "will not change anything", even though it is not what it means. -- SimenConsider this example: <snip>Oh well, that's obvious. f() can't call D.invMemberFunc() because D.invMemberFunc() isn't pure. Pure functions can only call other pure functions. You know that. I know that. Why would anyone think it strange?
Apr 09 2008
"Simen Kjaeraas" wroteOn Wed, 09 Apr 2008 15:40:37 +0200, Janice Caron wrote:I think there was a suggestion at one point to change const/invariant functions to be like: const(this) int f(); instead of const int f(); Which I think is much clearer. -SteveOn 09/04/2008, Jason House wrote:I think it is because invMemberFunc is invariant. Many read this as "will not change anything", even though it is not what it means.Consider this example: <snip>Oh well, that's obvious. f() can't call D.invMemberFunc() because D.invMemberFunc() isn't pure. Pure functions can only call other pure functions. You know that. I know that. Why would anyone think it strange?
Apr 09 2008
Simen Kjaeraas Wrote:On Wed, 09 Apr 2008 15:40:37 +0200, Janice Caron <caron800 googlemail.com> wrote:That's certainly a piece of it, but it's more than that. A lot of people talk about pure functions taking invariant data as being enough. In reality, the invariant nature is only enough when accessing data without member functions. When member functions are involved, the invariant property is not enough. While those of use paying attention know that it's not enough, it feels quite strange. Andrei's slides use multithreading as needing invariance to work. When pushed, people say that invariance is necessary but not sufficient and requires pure instead. So multithreading requires pure functions and invariant primitive types. The current const/invariant stuff has a way of handling member functions that really doesn't make much sense... It turns out that most invariant functions will also require that they're declared pure... so how much sense does invariant member functions really make? I'll toss out the idea that if invariant functions could only access invariant global state (and invariant static members) that they nearly match the pure function definition. In such a situation, nearly all invariant member functions could be used as part of multi-threaded optimization.On 09/04/2008, Jason House <jason.james.house gmail.com> wrote:I think it is because invMemberFunc is invariant. Many read this as "will not change anything", even though it is not what it means. -- SimenConsider this example: <snip>Oh well, that's obvious. f() can't call D.invMemberFunc() because D.invMemberFunc() isn't pure. Pure functions can only call other pure functions. You know that. I know that. Why would anyone think it strange?
Apr 09 2008
Simen Kjaeraas wrote:On Wed, 09 Apr 2008 15:40:37 +0200, Janice Caron wrote:I seriously think the problem is with the phrase "invMemberFunc is invariant". This leads people to think invariantness is a property of the function, instead of it merely requiring an invariant "this". Maybe we should figure out another way of stating it.You know that. I know that. Why would anyone think it strange?I think it is because invMemberFunc is invariant. Many read this as "will not change anything", even though it is not what it means.
Apr 09 2008
Georg Wrede Wrote:Simen Kjaeraas wrote:Is that really the best solution? Why not have do what people nievely think it does? The code optimizer would benefit from that as well.On Wed, 09 Apr 2008 15:40:37 +0200, Janice Caron wrote:I seriously think the problem is with the phrase "invMemberFunc is invariant". This leads people to think invariantness is a property of the function, instead of it merely requiring an invariant "this". Maybe we should figure out another way of stating it.You know that. I know that. Why would anyone think it strange?I think it is because invMemberFunc is invariant. Many read this as "will not change anything", even though it is not what it means.
Apr 09 2008
"Jason House" <jason.james.house gmail.com> wrote in message news:ftiuj8$mob$1 digitalmars.com...Georg Wrede Wrote:I agree.. And one less keyword, which, it seems, is a goal in itself. L.Simen Kjaeraas wrote:Is that really the best solution? Why not have do what people nievely think it does? The code optimizer would benefit from that as well.On Wed, 09 Apr 2008 15:40:37 +0200, Janice Caron wrote:I seriously think the problem is with the phrase "invMemberFunc is invariant". This leads people to think invariantness is a property of the function, instead of it merely requiring an invariant "this". Maybe we should figure out another way of stating it.You know that. I know that. Why would anyone think it strange?I think it is because invMemberFunc is invariant. Many read this as "will not change anything", even though it is not what it means.
Apr 09 2008
Lionello Lunesu wrote:"Jason House" <jason.james.house gmail.com> wrote in message news:ftiuj8$mob$1 digitalmars.com...I wasn't thinking of changing D, I was thinking more what we should *say* when we talk about invariantness and functions. The phrase "f is invariant" really does not say that it wants something "invariant". In English it says that "there is a property invariantness, that f is". It's like saying somebody is Chinese. That is not the same as saying she wants Chinese food. Any newcomer to this NG who doesn't have an extensive cultural background in C-like languages, will have a hard time before he realises that "f is invariant" actually only means "f loves an invariant 'this'".Georg Wrede Wrote:I agree.. And one less keyword, which, it seems, is a goal in itself.Simen Kjaeraas wrote:Is that really the best solution? Why not have do what people nievely think it does? The code optimizer would benefit from that as well.On Wed, 09 Apr 2008 15:40:37 +0200, Janice Caron wrote:I seriously think the problem is with the phrase "invMemberFunc is invariant". This leads people to think invariantness is a property of the function, instead of it merely requiring an invariant "this". Maybe we should figure out another way of stating it.You know that. I know that. Why would anyone think it strange?I think it is because invMemberFunc is invariant. Many read this as "will not change anything", even though it is not what it means.
Apr 10 2008
Georg Wrede Wrote:Lionello Lunesu wrote:Right, but I am ;)"Jason House" <jason.james.house gmail.com> wrote in message news:ftiuj8$mob$1 digitalmars.com...I wasn't thinking of changing D,Georg Wrede Wrote:I agree.. And one less keyword, which, it seems, is a goal in itself.Simen Kjaeraas wrote:Is that really the best solution? Why not have do what people nievely think it does? The code optimizer would benefit from that as well.On Wed, 09 Apr 2008 15:40:37 +0200, Janice Caron wrote:I seriously think the problem is with the phrase "invMemberFunc is invariant". This leads people to think invariantness is a property of the function, instead of it merely requiring an invariant "this". Maybe we should figure out another way of stating it.You know that. I know that. Why would anyone think it strange?I think it is because invMemberFunc is invariant. Many read this as "will not change anything", even though it is not what it means.I was thinking more what we should *say* when we talk about invariantness and functions.If we have to say special qualifiers every time we talk about stuff, it's a broken definition :( The less corner cases there are, the easier it'll be to discuss, the easier it'll be to learn, and the lower the potential for compiler bugs will be...
Apr 10 2008
Jason House wrote:Consider this example: class D{ void invMemberFunc() invariant; // not pure } class C{ int f(invariant D) invariant pure{ D.invMemberFunc(); // illegal } }((Upon proofreading it dawned to me that the example above may contain errors, but for the sake of clarity, somebody still explain.)) Not being an expert on this stuff, I have to ask: void invMemberFunc() invariant; // not pure What does it actually mean? A function taking no arguments, returning nothing? If it has no side effect, then it has no bearing on the program, therefore, it essentially is a null function, doing nothing. Or, if it has side effects, then the whole question is about: Do we allow or disallow pure functions calling member functions with intra-object side-effects. (And as far as I understand it, currently this is considered illegal, but just may become legal in D3 -- if it /really/ then seems like a warranted idea.) (1) And then it is /invariant/. What exactly does the word invariant mean in a function definition when it's after the function name? That it requires the argument to be an invariant? (I sure hope it's not some property "invariant" of the function, meaning somehow that it doesn't change (whatever)). Along the same lines (please anybody explain), int f(invariant D) invariant { ... } //omitting pure here for now What does that mean? Like, one would understand the latter invariant on the line to mean that you only can pass invariant data to the function, but then the former invariant on the line would be superfluous, right? And then, int f(invariant D) invariant pure{ ... } If the function is pure, then should that now implicitly demand an invariant argument? (At least as far as I've understood anything about this D version.) And therefore actually /both/ invariants are superfluous in the definition. Finally,class D{ void invMemberFunc() invariant; // not pure } class C{ int f(invariant D) invariant pure{ D.invMemberFunc(); // illegal } }Since invMemberFunc is not pure, then using it in f should really be illegal. Syntactically, that is, per definition. This because other alternatives would be too complicated for the compiler (and the programmer) to be practical. The above example might be clearer (at least to me :-), and depending of course on what exactly you are asking... ) if it said class D { int invMemberFunc(int i) invariant; // not pure } class C { int e; invariant int g; int f(invariant D d) invariant pure{ e = d.invMemberFunc(g); // illegal } } But again, I'm not expert enough to know for sure what you are asking here, so this may not be equivalent. But, if it were like this, then using invMemberFunc should still be illegal. --- (1) Which leads to the tought: since a pure function can't call other functions to use their side effects, the only reason left to call a function is to get it's value. From which follows that calling void functions should be made illegal! On the grounds that there is no point in calling such a function in the first place. (This as part of the "barriers" mentioned in the post 69190 "What is pure and what is not pure".)
Apr 09 2008
On 09/04/2008, Georg Wrede <georg nospam.org> wrote:void invMemberFunc() invariant; // not pure What does it actually mean?The word "invariant", in this position, means that the function's hidden "this" parameter is typed invariant.A function taking no arguments, returning nothing? If it has no side effect,The compiler doesn't know it has no side effects, because it's not declared pure, so it has to err on the side of caution.Do we allow or disallow pure functions calling member functions with intra-object side-effects.We disallow pure functions from calling non-pure functions.And then it is /invariant/. What exactly does the word invariant mean in a function definition when it's after the function name?See above. It means that "this" is invariant. From the caller's point of view, it means that the member function cannot be called from a non-invariant instance.Along the same lines (please anybody explain), int f(invariant D) invariant { ... } //omitting pure here for now What does that mean?Nothing. It's not legal D.int f(invariant D) invariant pure{ ... }Also not legal D. Although the following would be int f(invariant D d) { ... } invariant pure And that would mean that f takes as it's parameters the hidden invariant parameter "this" and an invariant D d. It is pure, and returns an int.If the function is pure, then should that now implicitly demand an invariant argument?That's been suggested before. No reply from the Powers That Be yet.Finally, Since invMemberFunc is not pure, then using it in f should really be illegal.Yes(1) Which leads to the tought: since a pure function can't call other functions to use their side effects, the only reason left to call a function is to get it's value. From which follows that calling void functions should be made illegal!Well, they can be optimised away, at least.
Apr 09 2008
Joining Janice's two replies. Janice Caron wrote:On 09/04/2008, Georg Wrede <georg nospam.org> wrote:Ok.void invMemberFunc() invariant; // not pure What does it actually mean?The word "invariant", in this position, means that the function's hidden "this" parameter is typed invariant.Ok.A function taking no arguments, returning nothing? If it has no side effect,The compiler doesn't know it has no side effects, because it's not declared pure, so it has to err on the side of caution.Ok.Do we allow or disallow pure functions calling member functions with intra-object side-effects.We disallow pure functions from calling non-pure functions.Ok.And then it is /invariant/. What exactly does the word invariant mean in a function definition when it's after the function name?See above. It means that "this" is invariant. From the caller's point of view, it means that the member function cannot be called from a non-invariant instance.Right.Along the same lines (please anybody explain), int f(invariant D) invariant { ... } //omitting pure here for now What does that mean?My apologies. Of course it's legal. I was misreading. (Although you did miss out the variable name for D. Let's call it d)So it means that "this" is invariant, and that d is invariant.To be precise, it /requires/ that "this" is invariant, right?Which is precisely the same as the previous one? Additionally, I seem to remember that stuff like kljal alsje fss laskef(kajs aks) { // potentially hundreds of lines here } lksjd afaf was frowned upon in D. Has this changed? The point being, it's bad to have some part of the signature geographically remote from the rest.int f(invariant D) invariant pure{ ... }Also I screwed up. Yes, that is legal D. My apologies again. It means as above, plus the function is also pure. int f(invariant D d) { ... } invariant pure And that would mean that f takes as it's parameters the hidden invariant parameter "this" and an invariant D d. It is pure, and returns an int.Maybe it's too obvious to even comment upon? :-) I mean, what if it's not implicit? Then folks doing pure functions would have to write invariant all over the place, *PLUS*, any newcomer would take it for granted that since you have to write it, then there *can* be pure functions not taking invariants! So, IMNSHO, there's no other way than to make them implicit, and therefore forbidden as redundant.If the function is pure, then should that now implicitly demand an invariant argument?That's been suggested before. No reply from the Powers That Be yet.Good.Finally, Since invMemberFunc is not pure, then using it in f should really be illegal.YesThey can be flatly removed! If they don't return a value, and don't change anything, what's left?! Actually, to be more precise: - A pure function can only call other pure functions. - Any pure function has to return a value. - In a pure function, ignoring the return value of a called function is an error. This is even stronger than (1) above, it is clearer, and it's *right*.(1) Which leads to the thought: since a pure function can't call other functions to use their side effects, the only reason left to call a function is to get it's value. From which follows that calling void functions should be made illegal!Well, they can be optimised away, at least.
Apr 09 2008
On 09/04/2008, Georg Wrede <georg nospam.org> wrote:No, again, that we my fault for misreading the question, and for which I now feel very silly. The qualifiers are /not/ allowed following the function body, and I apologise for giving that impression. However, the following are all equivalent. int f(invariant D d) invariant pure { ... } int f(invariant D d) pure invariant { ... } pure int f(invariant D d) invariant { ... } invariant int f(invariant D d) pure { ... } pure invariant int f(invariant D d) { ... } invariant pure int f(invariant D d) { ... } ...unless of course pure implies invariant, which we would hope, in which case you only have one of int f(D d) pure { ... } pure int f(D d) { ... }int f(invariant D d) { ... } invariant pure And that would mean that f takes as it's parameters the hidden invariant parameter "this" and an invariant D d. It is pure, and returns an int.Which is precisely the same as the previous one?Additionally, I seem to remember that stuff like kljal alsje fss laskef(kajs aks) { // potentially hundreds of lines here } lksjd afaf was frowned upon in D. Has this changed?No. Apologies for confusion.That's what "optimised away" means. :-)Well, they can be optimised away, at least.They can be flatly removed!- Any pure function has to return a value.To be /useful/ it has to return a value. To be /syntactically correct/, probably not. For example: void doNothing(int x) pure { } int doSomething(int x) pure { doNothing(x); return x + 1; } That looks OK to me. doNothing() will be optimised away completely, but I think it needs to be /allowed/ because such a function could conceivably arise as a result of template instantiation or string mixin.- In a pure function, ignoring the return value of a called function is an error.I see no reason why that should be an error. Again, generic programming might result in such code.This is even stronger than (1) above, it is clearer, and it's *right*.Sure, but you don't need to outlaw code that does nothing. :-)
Apr 09 2008
Janice Caron Wrote:On 09/04/2008, Georg Wrede <georg nospam.org> wrote:D already outlaws having code that isn't run. I would prefer for code that does nothing to be considered an error. Of course, with most functions being impure, that's tough to enforce. With pure functions, it should be easy.This is even stronger than (1) above, it is clearer, and it's *right*.Sure, but you don't need to outlaw code that does nothing. :-)
Apr 09 2008
"Jason House" <jason.james.house gmail.com> wrote in message news:ftislk$3m0$1 digitalmars.com...Janice Caron Wrote:Why? If pure-ness (uh, purity?) can be checked, it can also be deducted (*), so the compiler could optimize/complain in either case. Tagging a function explicitely as "pure" would only enable a constraint as to what other functions can be called, which is something I'd rather see solved using more generalized tagging abilities (See Scott Meyers' presentation on red-code, green-code [1]). Maybe identifiers between the parameter list and the function body could work as ad-hoc constraints. This would work for pure and nothrow, and the user can add whatever identifier/constraint s/he wants. L. (*) Is this conclusion correct? It feels correct, but I don't think I can proof it. [1] http://www.nwcpp.org/Downloads/2007/redcode_-_updated.pdfOn 09/04/2008, Georg Wrede <georg nospam.org> wrote:D already outlaws having code that isn't run. I would prefer for code that does nothing to be considered an error. Of course, with most functions being impure, that's tough to enforce. With pure functions, it should be easy.This is even stronger than (1) above, it is clearer, and it's *right*.Sure, but you don't need to outlaw code that does nothing. :-)
Apr 09 2008
Lionello Lunesu wrote:"Jason House" <jason.james.house gmail.com> wrote in message news:ftislk$3m0$1 digitalmars.com...I was thinking along the same lines last week or so. A lot of things about a method signature can be deduced from what's inside. Including types of the arguments in many cases. Languages like ML take this to the extreme. But the point of putting the info explicitly in the signature is so that A) the compiler doesn't need to see the implementation right away to tell what's inside (separate compilation). B) Joe Programmer doesn't have to see the implementation to know if he can call it or not (documentation). C) Fred Programmer can declare his intent to Joe that the method work in such a way, and the compiler can tell Fred if what he though was true is not (verification). CTFE-ness is automatically deduced by the compiler, but using CTFE always requires access to the source of the method so A) is not an issue. Still it might be nice to have a "ctfe" tag for purpose B). So I think deduction of such stuff is not really viable overall. Perhaps it could be allowed for templates though? The source is required to be available for those anyway. --bbJanice Caron Wrote:Why? If pure-ness (uh, purity?) can be checked, it can also be deducted (*), so the compiler could optimize/complain in either case.On 09/04/2008, Georg Wrede <georg nospam.org> wrote:D already outlaws having code that isn't run. I would prefer for code that does nothing to be considered an error. Of course, with most functions being impure, that's tough to enforce. With pure functions, it should be easy.This is even stronger than (1) above, it is clearer, and it's *right*.Sure, but you don't need to outlaw code that does nothing. :-)
Apr 09 2008
On 10/04/2008, Lionello Lunesu <lionello lunesu.remove.com> wrote:No I did not. That was Jason House. Please be careful with your level of nesting!Janice Caron Wrote: ... D already outlaws having code that isn't run. I would prefer for code that does nothing to be considered an error. Of course, with most functions being impure, that's tough to enforce. With pure functions, it should be>easy.
Apr 09 2008
"Janice Caron" <caron800 googlemail.com> wrote in message news:mailman.374.1207809416.2351.digitalmars-d puremagic.com...On 10/04/2008, Lionello Lunesu <lionello lunesu.remove.com> wrote:Uh, the "..." part contains what you've said, not what follows it, so the quote was right (at least it looks just fine in my reader). L.No I did not. That was Jason House. Please be careful with your level of nesting!Janice Caron Wrote: ... D already outlaws having code that isn't run. I would prefer for code that does nothing to be considered an error. Of course, with most functions being impure, that's tough to enforce. With pure functions, it should be>easy.
Apr 10 2008
On 10/04/2008, Lionello Lunesu <lionello lunesu.remove.com> wrote:Why? If pure-ness (uh, purity?) can be checked, it can also be deducted"purity" is the right word. (But "deducted" should be "deduced" :-))(*), so the compiler could optimize/complain in either case.It would slow down compilation no end though. For example, a pure function is not allowed to call non-pure functions. Without the "pure" keyword, you'd have to follow through every function call recursively, and that could take a long time. Moreover, just because the compiler decides the function is impure, that doesn't necessarily mean that the programmer /intended/ for it to be impure. Perhaps there was a bug? Perhaps there was a silly, simple, one line bug, that the programmer could fix in a jiffy if the compiler would only say "Error: Line 53: Pure function cannot do XYZ"? It is far better for the programmer to document their intentions, so that the compiler can check that those intentions are actually met.
Apr 09 2008
"Janice Caron" <caron800 googlemail.com> wrote in message news:mailman.375.1207810102.2351.digitalmars-d puremagic.com...On 10/04/2008, Lionello Lunesu <lionello lunesu.remove.com> wrote::-) thanks.Why? If pure-ness (uh, purity?) can be checked, it can also be deducted"purity" is the right word. (But "deducted" should be "deduced" :-))Yeah, but you'll have to visit those functions anyway, because you're compiling. Granted, header files should explicitely mention pure/notpure, and to make it safe, it must be mangled.(*), so the compiler could optimize/complain in either case.It would slow down compilation no end though. For example, a pure function is not allowed to call non-pure functions. Without the "pure" keyword, you'd have to follow through every function call recursively, and that could take a long time.Moreover, just because the compiler decides the function is impure, that doesn't necessarily mean that the programmer /intended/ for it to be impure. Perhaps there was a bug? Perhaps there was a silly, simple, one line bug, that the programmer could fix in a jiffy if the compiler would only say "Error: Line 53: Pure function cannot do XYZ"? It is far better for the programmer to document their intentions, so that the compiler can check that those intentions are actually met.Good point. L.
Apr 10 2008
Lionello Lunesu wrote:"Janice Caron" <caron800 googlemail.com> wrote in message news:mailman.375.1207810102.2351.digitalmars-d puremagic.com...Visited? Yes. Look at their body? Not if they're in a different module, AFAICT. If the function is in another module then the declaration should be enough (the fact that .di "headers" work confirms this), and the body can effectively be ignored beyond checking syntactical correctness (i.e. checking that it *could* be valid code provided all identifiers it uses resolve to stuff of appropriate types, but not actually looking up any of the identifiers mentioned in it).On 10/04/2008, Lionello Lunesu <lionello lunesu.remove.com> wrote::-) thanks.Why? If pure-ness (uh, purity?) can be checked, it can also be deducted"purity" is the right word. (But "deducted" should be "deduced" :-))Yeah, but you'll have to visit those functions anyway, because you're compiling. Granted, header files should explicitely mention pure/notpure, and to make it safe, it must be mangled.(*), so the compiler could optimize/complain in either case.It would slow down compilation no end though. For example, a pure function is not allowed to call non-pure functions. Without the "pure" keyword, you'd have to follow through every function call recursively, and that could take a long time.
Apr 10 2008
Janice Caron wrote:On 09/04/2008, Georg Wrede <georg nospam.org> wrote: However, the following are all equivalent. int f(invariant D d) invariant pure { ... } int f(invariant D d) pure invariant { ... } pure int f(invariant D d) invariant { ... } invariant int f(invariant D d) pure { ... } pure invariant int f(invariant D d) { ... } invariant pure int f(invariant D d) { ... }Thanks. (Although I'd really love to see less possibilities here. Grepping for code, quickly skimming others' code, and for me personally, making less mistakes, ...)...unless of course pure implies invariant, which we would hope, in which case you only have one of int f(D d) pure { ... } pure int f(D d) { ... }Excellent.:-)That's what "optimised away" means. :-)Well, they can be optimised away, at least.They can be flatly removed!In the same manner as unreachable code is an error with many compilers, this might be polite. But, I do believe the template issue!- Any pure function has to return a value.To be /useful/ it has to return a value. To be /syntactically correct/, probably not. For example: void doNothing(int x) pure { } int doSomething(int x) pure { doNothing(x); return x + 1; } That looks OK to me. doNothing() will be optimised away completely, but I think it needs to be /allowed/ because such a function could conceivably arise as a result of template instantiation or string mixin.- In a pure function, ignoring the return value of a called function is an error.I see no reason why that should be an error. Again, generic programming might result in such code.This is even stronger than (1) above, it is clearer, and it's *right*.Sure, but you don't need to outlaw code that does nothing. :-)
Apr 09 2008
Janice Caron Wrote:On 09/04/2008, Georg Wrede <georg nospam.org> wrote: int f(invariant D d) invariant pure { ... }Shouldn't it be int f(invariant D d) pure { ... } Pure functions are a subset of invariant functions, no?
Apr 09 2008
guslay wrote:Janice Caron Wrote:Just a nitpick, but "pure" applies to the function as a whole. So there's no reason not to put it at the head with 'static' and protection levels. There's no 'pure int'. It's not going to be a type constructor. Just a storage class. AFAIK. --bbOn 09/04/2008, Georg Wrede <georg nospam.org> wrote: int f(invariant D d) invariant pure { ... }Shouldn't it be int f(invariant D d) pure { ... } Pure functions are a subset of invariant functions, no?
Apr 09 2008
"guslay" wroteJanice Caron Wrote:No. A function does not necessarily need to take an invariant 'this' pointer to be pure. For example: class C { invariant int x; pure int getX() { return x;} } void foo() { C c = new C; c.getX(); // ok } -SteveOn 09/04/2008, Georg Wrede wrote: int f(invariant D d) invariant pure { ... }Shouldn't it be int f(invariant D d) pure { ... } Pure functions are a subset of invariant functions, no?
Apr 09 2008
On 09/04/2008, Steven Schveighoffer <schveiguy yahoo.com> wrote:A function does not necessarily need to take an invariant 'this' pointer to be pure. For example: <snip>Trouble is, that means we'll end up having to write "invariant" all over the place, and it's really quite a long keyword! Maybe Walter will let us shorten it to "in"? (Yes, I know "in" means "const" right now).
Apr 09 2008
Steven Schveighoffer Wrote:"guslay" wrotec.getX() is equivalent to " int getX( ref C this ) ", so its not pure. I will restate: Pure member functions are a subset of invariant member functions.Janice Caron Wrote:No. A function does not necessarily need to take an invariant 'this' pointer to be pure. For example: class C { invariant int x; pure int getX() { return x;} } void foo() { C c = new C; c.getX(); // ok } -SteveOn 09/04/2008, Georg Wrede wrote: int f(invariant D d) invariant pure { ... }Shouldn't it be int f(invariant D d) pure { ... } Pure functions are a subset of invariant functions, no?
Apr 09 2008
"guslay" wroteSteven Schveighoffer Wrote:AFAIK, C.getX() is equivalent to int getX(C this). i.e. you can't change the this pointer to point to something else during the function. I don't see why it's not pure. I'm questioning the assertion that pure functions MUST take invariant reference types. They can take mutable ones as long as they do not reference mutable data within the type. It's the same reason you will be able to pass strings to a pure function. A string is mutable, but the data it points to is not. -Steve"guslay" wrotec.getX() is equivalent to " int getX( ref C this ) ", so its not pure.Janice Caron Wrote:No. A function does not necessarily need to take an invariant 'this' pointer to be pure. For example: class C { invariant int x; pure int getX() { return x;} } void foo() { C c = new C; c.getX(); // ok } -SteveOn 09/04/2008, Georg Wrede wrote: int f(invariant D d) invariant pure { ... }Shouldn't it be int f(invariant D d) pure { ... } Pure functions are a subset of invariant functions, no?
Apr 09 2008
Steven Schveighoffer Wrote:AFAIK, C.getX() is equivalent to int getX(C this). i.e. you can't change the this pointer to point to something else during the function. I don't see why it's not pure. I'm questioning the assertion that pure functions MUST take invariant reference types. They can take mutable ones as long as they do not reference mutable data within the type. It's the same reason you will be able to pass strings to a pure function. A string is mutable, but the data it points to is not. -SteveI can't think of a counter-example, you might be correct.
Apr 09 2008
On 09/04/2008, Steven Schveighoffer <schveiguy yahoo.com> wrote:It's the same reason you will be able to pass strings to a pure function. A string is mutable, but the data it points to is not.No, that's because of Andrei's RULE 3: T implicitly converts to and from invariant(T) iff T refers to no mutable memory (from accu-functional.pdf) Therefore, string implicitly casts to invariant(string). Perhaps then, we may say that the parameters of a pure function should be invariant, or implicitly castable to invariant? Of course, it's /possible/ to write a pure function that takes mutable pointers, e.g. int f(char[] s) pure? { return 42; } but do we care? Wouldn't it just be easier to make that illegal and insist that the programmer rewrite the function more sensibly?
Apr 09 2008
"Janice Caron" wroteOn 09/04/2008, Steven Schveighoffer wrote:Hm.. I glossed over that rule :) But by the same rule, my example class C should be implicitly castable to invariant, but in the case where C has invariant and mutable parts, then this is not the case... I was thinking a different rule (for pure functions) would be more appropriate: Disallow access to non-invariant data Should be changed to: Only allow access to invariant and local data.It's the same reason you will be able to pass strings to a pure function. A string is mutable, but the data it points to is not.No, that's because of Andrei's RULE 3: T implicitly converts to and from invariant(T) iff T refers to no mutable memory(from accu-functional.pdf) Therefore, string implicitly casts to invariant(string). Perhaps then, we may say that the parameters of a pure function should be invariant, or implicitly castable to invariant?Hopefully not the former, as working with invariant data when one doesn't have to makes things really cumbersome. If it's implicitly castable to invariant, then it's implicitly castable from invariant, I'd rather have the mutable version :)Of course, it's /possible/ to write a pure function that takes mutable pointers, e.g. int f(char[] s) pure? { return 42; } but do we care? Wouldn't it just be easier to make that illegal and insist that the programmer rewrite the function more sensibly?I'm thinking more of the case where an argument is a mutable stack pointer (meaning it is a pointer, but only exists in the pure function's stack) to a chunk of data that has both mutable and invariant pieces. i.e.: class C { int *m; invariant(int *) i; } pure int getI(C c) { return *i; } There may be a reason to use i, but not m in a pure function. Why should pure restrict this? It's technically correct and statically verifiable. -Steve
Apr 09 2008
On 09/04/2008, Steven Schveighoffer <schveiguy yahoo.com> wrote:class C { int *m; invariant(int *) i; } pure int getI(C c) { return *i; } There may be a reason to use i, but not m in a pure function. Why should pure restrict this? It's technically correct and statically verifiable.Except that there's no way to initialise i. My understanding of the whole raw vs cooked thing in accu-functional.pdf is that it's a way of making an entirely invariant class. There would still be no way to create a class with some members invariant and others not, except for those that can be initialised at compile time.
Apr 09 2008
"Janice Caron" wroteOn 09/04/2008, Steven Schveighoffer wrote:I think this is a limitation that should be lifted, as there may be reasons to have an invariant member of a class that is set upon construction. I can't see any reason to disallow it. -Steveclass C { int *m; invariant(int *) i; } pure int getI(C c) { return *i; } There may be a reason to use i, but not m in a pure function. Why should pure restrict this? It's technically correct and statically verifiable.Except that there's no way to initialise i. My understanding of the whole raw vs cooked thing in accu-functional.pdf is that it's a way of making an entirely invariant class. There would still be no way to create a class with some members invariant and others not, except for those that can be initialised at compile time.
Apr 09 2008
On 09/04/2008, Steven Schveighoffer <schveiguy yahoo.com> wrote:A function does not necessarily need to take an invariant 'this' pointer to be pure. For example: class C { invariant int x; pure int getX() { return x;} } void foo() { C c = new C; c.getX(); // ok }OK, I'm gonna risk it. :-) Andrei proposes a new feature, *invariant constructors*, which are constructors with special rules to ensure that pointers to mutable memory, however, my interpretation of accu-functional.pdf is that invariant constructors will cause the /entire class/ to be created invariant. Thus, one would write class C { int x; this(int n) invariant { x = n; } int getX() invariant pure { return x; } } invariant c = new C(4); int n = c.getX; My interpretation is that the invariant constructor will construct a /completely invariant/ object. In that case, there can never be any such thing as a non-invariant C. What you're describing is something different - a non-invariant class with invariant members. Well, the only time you can assign an invariant member is at compile-time. That means they have to be assigned with compile-time constants. That, in turn, means they might as well be static, because they are not going to differ from instance to instance. And /that/ means you can rewrite your counterexample as class C { static invariant int x = 4; static int getX() pure { return x; } } auto c = new C(4); int n = c.getX; Now getX() is pure because it's static, and therefore /has/ no this pointer. In conclusion, it may well be that D will insist that all parameters of pure functions be completely invariant. I don't know that for /sure/, but that would be my guess.
Apr 09 2008
On Wed, 09 Apr 2008 20:40:38 +0200, Janice Caron <caron800 googlemail.co= m> = wrote:What you're describing is something different - a non-invariant class with invariant members. Well, the only time you can assign an invariant member is at compile-time. That means they have to be assigned with compile-time constants. That, in turn, means they might as well be static, because they are not going to differ from instance to instance. And /that/ means you can rewrite your counterexample as class C { static invariant int x =3D 4; static int getX() pure { return x; } } auto c =3D new C(4); int n =3D c.getX; Now getX() is pure because it's static, and therefore /has/ no this =pointer.class foo { invariant int bar; = this() { bar =3D 4; } } This works under DMD 2.012. However, this does not work: class foo { invariant int bar =3D 2; = this() { bar =3D 4; } } There is nothing wrong with having non-static, invariant member variable= s, = which are assigned in the constructor. Of course, you'd get the exact sa= me = result (except for function which require invariant parameters, i.e. pur= e = functions) with a const int. -- Simen
Apr 09 2008
On 09/04/2008, Simen Kjaeraas <simen.kjaras gmail.com> wrote:class foo { invariant int bar; this() { bar = 4; } } This works under DMD 2.012.That's interesting. I did not know that.
Apr 09 2008
On Thu, 10 Apr 2008 01:07:51 +0200, Janice Caron <caron800 googlemail.co= m> = wrote:On 09/04/2008, Simen Kjaeraas <simen.kjaras gmail.com> wrote:I can't quite see what would be the difference between const and invariant in this example. It's a member variable that is local to each instance, and cannot be changed. Why should it not work? -- Simenclass foo { invariant int bar; this() { bar =3D 4; } } This works under DMD 2.012.That's interesting. I did not know that.
Apr 10 2008
On 09/04/2008, guslay <guslay gmail.com> wrote:Janice Caron Wrote: > On 09/04/2008, Georg Wrede <georg nospam.org> wrote: >Hopefully, it will just be int f(D d) pure {...} and pure will imply, not only that "this" is invariant, but that all function arguments are too. However, at this stage we can only guess at what the syntax will be.int f(invariant D d) invariant pure { ... }Shouldn't it be int f(invariant D d) pure { ... }
Apr 09 2008
On 09/04/2008, Janice Caron <caron800 googlemail.com> wrote:> int f(invariant D) invariant { ... } //omitting pure here for now > > What does that mean? Nothing. It's not legal D.My apologies. Of course it's legal. I was misreading. (Although you did miss out the variable name for D. Let's call it d) So it means that "this" is invariant, and that d is invariant.Also not legal D. Although the following would beAlso I screwed up. Yes, that is legal D. My apologies again. It means as above, plus the function is also pure.
Apr 09 2008
"Georg Wrede" wroteJason House wrote:It means that the 'this' pointer is invariant. We don't see the body of the function, so we don't know if it's pure or not. However, the point is, since it is not *declared* pure (we don't know the correct syntax for this by the way because it doesn't exist yet!), the compiler doesn't know whether it has side effects or not. Remember, Walter and Andrei are trying to create a statically verifyable functional programming construct. This means that the compiler doesn't just *assume* that a pure function has no side effects, it *guarantees* that the function has no side effects.Consider this example: class D{ void invMemberFunc() invariant; // not pure } class C{ int f(invariant D) invariant pure{ D.invMemberFunc(); // illegal } }((Upon proofreading it dawned to me that the example above may contain errors, but for the sake of clarity, somebody still explain.)) Not being an expert on this stuff, I have to ask: void invMemberFunc() invariant; // not pure What does it actually mean? A function taking no arguments, returning nothing? If it has no side effect, then it has no bearing on the program, therefore, it essentially is a null function, doing nothing. Or, if it has side effects, then the whole question is about: Do we allow or disallow pure functions calling member functions with intra-object side-effects. (And as far as I understand it, currently this is considered illegal, but just may become legal in D3 -- if it /really/ then seems like a warranted idea.) (1) And then it is /invariant/. What exactly does the word invariant mean in a function definition when it's after the function name? That it requires the argument to be an invariant? (I sure hope it's not some property "invariant" of the function, meaning somehow that it doesn't change (whatever)).Along the same lines (please anybody explain), int f(invariant D) invariant { ... } //omitting pure here for now What does that mean? Like, one would understand the latter invariant on the line to mean that you only can pass invariant data to the function, but then the former invariant on the line would be superfluous, right?Not exactly, it is another way of saying that the 'this' pointer is invariant.And then, int f(invariant D) invariant pure{ ... } If the function is pure, then should that now implicitly demand an invariant argument? (At least as far as I've understood anything about this D version.) And therefore actually /both/ invariants are superfluous in the definition.Not necessarily :) A pure function can take non-invariant arguments, it just can't use the non-invariant pieces of that. Yeah, I know you just did a double take :) But think about this: f(int x) Does x need to be invariant for f to be pure? No, because every time we call x, we create a COPY of x on the stack, which means f has it's own private copy that isn't invariant, but f is guaranteed that nothing else will change it. Now if we have: f(int *x) Does x need to be invariant for f to be pure? Actually, no :) Because f is STILL given a stack variable, which happens to be a pointer to mutable data. If f dereferences x, then it cannot be pure unless x is invariant. See the difference? Now the real crux of the issue: class C { invariant int x; int y; } f(C c) Does c need to be invariant for f to be pure? No. Because c is still a stack variable, which is a reference to an instance of C. However, in order for f to be declared pure, it can only access c.x, it cannot access c.y, because c.y might change. What about structs? struct S { int x; } f(S s) Again, s does not need to be invariant, because the entire struct is copied onto the stack. This is similar to the f(int) case. f(S *s) Now, f can still be pure, but it is not allowed to dereference s. The rules for pure are: can only access: - local variables (mutable, invariant or const) - invariant data that is referenced by local variables (a class is intrinsically a reference, so class instances fall under this category) - pure functions There are interesting puzzles that I'm not sure how they will be solved. For example: pure int f() { char[] c = new char[15]; c[0] = 'h'; // does this compile? } Does c need to be invariant to access members of the array? Clearly from this code, you can see that c is private to f. But under the rules, the data c references is not invariant, and so should be inaccessible. How will the compiler make this distinction?Finally,Whether invMemberFunc takes or returns an int or not is irrelevant :) The fact that it is invariant means it can still might change global data, and so it might not be pure. It could be pure in the sense that it does not change global data, but because we didn't declare it pure, the compiler cannot know whether it is pure or not, and so it errs on the side of caution. Note that passing in an int and returning an int does not mean it can't be pure. I know you didn't get that when you wrote the post, but I want to emphasize it for other readers...class D{ void invMemberFunc() invariant; // not pure } class C{ int f(invariant D) invariant pure{ D.invMemberFunc(); // illegal } }Since invMemberFunc is not pure, then using it in f should really be illegal. Syntactically, that is, per definition. This because other alternatives would be too complicated for the compiler (and the programmer) to be practical. The above example might be clearer (at least to me :-), and depending of course on what exactly you are asking... ) if it said class D { int invMemberFunc(int i) invariant; // not pure } class C { int e; invariant int g; int f(invariant D d) invariant pure{ e = d.invMemberFunc(g); // illegal } }(1) Which leads to the tought: since a pure function can't call other functions to use their side effects, the only reason left to call a function is to get it's value. From which follows that calling void functions should be made illegal! On the grounds that there is no point in calling such a function in the first place. (This as part of the "barriers" mentioned in the post 69190 "What is pure and what is not pure".)This is true, calling a pure function which returns nothing makes no sense, but clearly this function can be pure: void f() {return;} as it has no side effects :) Should it be illegal? I'd say no. Useless, but not illegal. -Steve
Apr 09 2008
Steven Schveighoffer wrote:There are interesting puzzles that I'm not sure how they will be solved. For example: pure int f() { char[] c = new char[15]; c[0] = 'h'; // does this compile? } Does c need to be invariant to access members of the array? Clearly from this code, you can see that c is private to f. But under the rules, the data c references is not invariant, and so should be inaccessible. How will the compiler make this distinction?Before commenting on the rest, to me it is obvious that c is solely owned by f (because no references to c can exist outside of f), and therefore c is considered internal to f, so it's legal.
Apr 09 2008
"Georg Wrede" wroteSteven Schveighoffer wrote:The whole problem comes from the parsing. I'm not a guru when it comes to how D's grammar is structured, but I think it's supposed to be 'context free'? So in the single statement: c[0] = 'h'; and c's type is char[], how can the compiler be sure it's pointing to local data without context/analysis? In my mind, I think that it should be allowed, but I'm just not sure how that can be implemented in a guaranteed fashion, and still keep the grammar context-free. If c has it's own special 'pure' type, that's one thing, but I'm not sure how that works. Maybe it just tags c as being local internally, just to check for purity... -SteveThere are interesting puzzles that I'm not sure how they will be solved. For example: pure int f() { char[] c = new char[15]; c[0] = 'h'; // does this compile? } Does c need to be invariant to access members of the array? Clearly from this code, you can see that c is private to f. But under the rules, the data c references is not invariant, and so should be inaccessible. How will the compiler make this distinction?Before commenting on the rest, to me it is obvious that c is solely owned by f (because no references to c can exist outside of f), and therefore c is considered internal to f, so it's legal.
Apr 09 2008
Steven Schveighoffer wrote:"Georg Wrede" wroteWell, the localness of c is established with char[] c on the preceding line. And since D relies on bounds checking (not in release code, but in its Philosophy), c[0] is "local" too, for the purposes of this discussion.Steven Schveighoffer wrote:The whole problem comes from the parsing. I'm not a guru when it comes to how D's grammar is structured, but I think it's supposed to be 'context free'? So in the single statement: c[0] = 'h'; and c's type is char[], how can the compiler be sure it's pointing to local data without context/analysis?There are interesting puzzles that I'm not sure how they will be solved. For example: pure int f() { char[] c = new char[15]; c[0] = 'h'; // does this compile? } Does c need to be invariant to access members of the array? Clearly from this code, you can see that c is private to f. But under the rules, the data c references is not invariant, and so should be inaccessible. How will the compiler make this distinction?Before commenting on the rest, to me it is obvious that c is solely owned by f (because no references to c can exist outside of f), and therefore c is considered internal to f, so it's legal.In my mind, I think that it should be allowed, but I'm just not sure how that can be implemented in a guaranteed fashion, and still keep the grammar context-free. If c has it's own special 'pure' type, that's one thing, but I'm not sure how that works. Maybe it just tags c as being local internally, just to check for purity...The compiler has to keep track of local variables anyhow.
Apr 09 2008
Steven Schveighoffer wrote:"Georg Wrede" wroteOk. But I strongly resent being able to put the invariant either before or after the function name. It really gives the impression that it means something else.Jason House wrote:It means that the 'this' pointer is invariant. We don't see the body of the function, so we don't know if it's pure or not.Consider this example: class D{ void invMemberFunc() invariant; // not pure } class C{ int f(invariant D) invariant pure{ D.invMemberFunc(); // illegal } }((Upon proofreading it dawned to me that the example above may contain errors, but for the sake of clarity, somebody still explain.)) Not being an expert on this stuff, I have to ask: void invMemberFunc() invariant; // not pure What does it actually mean? A function taking no arguments, returning nothing? If it has no side effect, then it has no bearing on the program, therefore, it essentially is a null function, doing nothing. Or, if it has side effects, then the whole question is about: Do we allow or disallow pure functions calling member functions with intra-object side-effects. (And as far as I understand it, currently this is considered illegal, but just may become legal in D3 -- if it /really/ then seems like a warranted idea.) (1) And then it is /invariant/. What exactly does the word invariant mean in a function definition when it's after the function name? That it requires the argument to be an invariant? (I sure hope it's not some property "invariant" of the function, meaning somehow that it doesn't change (whatever)).However, the point is, since it is not *declared* pure (we don't know the correct syntax for this by the way because it doesn't exist yet!), the compiler doesn't know whether it has side effects or not. Remember, Walter and Andrei are trying to create a statically verifyable functional programming construct. This means that the compiler doesn't just *assume* that a pure function has no side effects, it *guarantees* that the function has no side effects.In A/W pure, where the reason for all this invariantness is optimization, it would seem hard to enforce, so it would be easier to simply forbid non-invariant arguments.And then, int f(invariant D) invariant pure{ ... }A pure function can take non-invariant arguments, it just can't use the non-invariant pieces of that. Yeah, I know you just did a double take :) But think about this:f(int x) Does x need to be invariant for f to be pure?Well, excluding A/W, the pureness of a function is within itself, and not in whether the arguments can change. But here it's different.No, because every time we call x, we create a COPY of x on the stack, which means f has it's own private copy that isn't invariant, but f is guaranteed that nothing else will change it. Now if we have:Generalising this, the point is to be able to make a local copy from data that potentially can change? Thus, the compiler can optimize relying on the now acquired invariantness.f(int *x) Does x need to be invariant for f to be pure? Actually, no :) Because f is STILL given a stack variable, which happens to be a pointer to mutable data. If f dereferences x, then it cannot be pure unless x is invariant. See the difference? Now the real crux of the issue: class C { invariant int x; int y; } f(C c) Does c need to be invariant for f to be pure? No. Because c is still a stack variable, which is a reference to an instance of C. However, in order for f to be declared pure, it can only access c.x, it cannot access c.y, because c.y might change.Which would mean that if f initially makes a local copy of c.y, then everyting would be ok?What about structs? struct S { int x; } f(S s) Again, s does not need to be invariant, because the entire struct is copied onto the stack. This is similar to the f(int) case.Ok. And the point being here is that a private copy is made. (Just making sure that _stack_ is not the keyword here. It might as well be on the heap (for example), /as long as/ f has the only reference to it. Of course normally it would be stack, of course.)f(S *s) Now, f can still be pure, but it is not allowed to dereference s.Which is useless, right? :-) But still "correct".Agreed.Finally,Whether invMemberFunc takes or returns an int or not is irrelevant :) The fact that it is invariant means it can still might change global data, and so it might not be pure. It could be pure in the sense that it does not change global data, but because we didn't declare it pure, the compiler cannot know whether it is pure or not, and so it errs on the side of caution.class D{ void invMemberFunc() invariant; // not pure } class C{ int f(invariant D) invariant pure{ D.invMemberFunc(); // illegal } }Since invMemberFunc is not pure, then using it in f should really be illegal. Syntactically, that is, per definition. This because other alternatives would be too complicated for the compiler (and the programmer) to be practical. The above example might be clearer (at least to me :-), and depending of course on what exactly you are asking... ) if it said class D { int invMemberFunc(int i) invariant; // not pure } class C { int e; invariant int g; int f(invariant D d) invariant pure{ e = d.invMemberFunc(g); // illegal } }If there wasn't for the risk that this needs to be allowed as a potential product of some template stuff, I'd say this should have been made illegal, in the same sense as unreachable code: it makes no sense.(1) Which leads to the thought: since a pure function can't call other functions to use their side effects, the only reason left to call a function is to get it's value. From which follows that calling void functions should be made illegal! On the grounds that there is no point in calling such a function in the first place. (This as part of the "barriers" mentioned in the post 69190 "What is pure and what is not pure".)This is true, calling a pure function which returns nothing makes no sense, but clearly this function can be pure: void f() {return;} as it has no side effects :) Should it be illegal? I'd say no. Useless, but not illegal.
Apr 09 2008
Jason House Wrote:Janice Caron Wrote:[Corrected example - Thanks george] class D{ int invMemberFunc() invariant; // not pure } class C{ int f(invariant D) invariant pure{ return D.invMemberFunc(); // illegal } }On 09/04/2008, Jason House <jason.james.house gmail.com> wrote:Consider this example:The strange thing is that pure functions can't call invariant member functions of their invariant data.I don't think that's correct. [example showing misinterpretation of what I'm talking about]
Apr 09 2008
On Wed, 09 Apr 2008 13:50:59 +0100, Janice Caron wrote:On 09/04/2008, Jason House <jason.james.house gmail.com> wrote:Couldn't the compiler insert the cast based on the declaration? /* compiler inserts the cast since the declaration is invariant */ invariant c = new C; This would also make sense for const. Is this possible? I think it would be a nice nugget of syntactic sugar.The strange thing is that pure functions can't call invariant member functions of their invariant data.I don't think that's correct. class C { int x; int f() invariant pure { return x; } } invariant c = cast(invariant) new C; int n = c.f(); Should work just fine. Of course, the explicit cast necessary to create an invariant C in the first place is a bit ugly. Maybe we need "inew" to make new invariant objects?
Apr 09 2008
On 09/04/2008, Denton Cockburn <diboss hotmail.com> wrote:> Of course, the explicit cast necessary to > create an invariant C in the first place is a bit ugly. Maybe we need > "inew" to make new invariant objects? Couldn't the compiler insert the cast based on the declaration?No, because objects created by new are not necessarily transitively unique. For example class C { int * p; this() { p = &someGlobalVariable; } } invariant C c = cast(invariant) new C; someGlobalVariable = 1; Whoops! c just changed! The explicit cast makes it the programmer's fault, not the compiler's! Come to think of it, "inew" would suffer the exact same problem, so it doesn't solve anything. Looks like there's no easy way to make an invariant class instance.
Apr 09 2008
"Janice Caron" wroteOn 09/04/2008, Denton Cockburn wrote:Sure there is :) class C { int *p; int q; invariant this() // means, 'this' is going to be invariant { //p = &someGlobalVariable; // fails, p must point to invariant q = 5; // allowed by compiler because it's the first time you set q. p = &q; // allowed because q is invariant. } } I think this was outlined in Andrei's presentation about grafting functional languages onto imperitive languages. Of course, it doesn't work today, but I think it would work. In fact, I think if you have a class like: class X{} class C { invariant X x; } There is no way to set x anywhere... I think this needs to be addressed. -Steve> Of course, the explicit cast necessary to > create an invariant C in the first place is a bit ugly. Maybe we need > "inew" to make new invariant objects? Couldn't the compiler insert the cast based on the declaration?No, because objects created by new are not necessarily transitively unique. For example class C { int * p; this() { p = &someGlobalVariable; } } invariant C c = cast(invariant) new C; someGlobalVariable = 1; Whoops! c just changed! The explicit cast makes it the programmer's fault, not the compiler's! Come to think of it, "inew" would suffer the exact same problem, so it doesn't solve anything. Looks like there's no easy way to make an invariant class instance.
Apr 09 2008
Janice Caron wrote:On 09/04/2008, Denton Cockburn <diboss hotmail.com> wrote:It should be illegal to cast objects invariant unless the compiler can guarantee the invariantness. So the cast here should produce an error.No, because objects created by new are not necessarily transitively unique. For example class C { int * p; this() { p = &someGlobalVariable; } } invariant C c = cast(invariant) new C; someGlobalVariable = 1; Whoops! c just changed! The explicit cast makes it the programmer's fault, not the compiler's! Come to think of it, "inew" would suffer the exact same problem, so it doesn't solve anything. Looks like there's no easy way to make an invariant class instance.Of course, the explicit cast necessary to create an invariant C in the first place is a bit ugly. Maybe we need "inew" to make new invariant objects?Couldn't the compiler insert the cast based on the declaration?
Apr 09 2008
"Georg Wrede" wroteJanice Caron wrote:This cannot be guaranteed by the compiler. It is depending on you to ensure that the object stays invariant. From http://www.digitalmars.com/d/2.0/const3.html "The second way (to create invariant data) is to cast data to invariant. When doing so, it is up to the programmer to ensure that no other mutable references to the same data exist." -SteveOn 09/04/2008, Denton Cockburn wrote:It should be illegal to cast objects invariant unless the compiler can guarantee the invariantness. So the cast here should produce an error.No, because objects created by new are not necessarily transitively unique. For example class C { int * p; this() { p = &someGlobalVariable; } } invariant C c = cast(invariant) new C; someGlobalVariable = 1; Whoops! c just changed! The explicit cast makes it the programmer's fault, not the compiler's! Come to think of it, "inew" would suffer the exact same problem, so it doesn't solve anything. Looks like there's no easy way to make an invariant class instance.Of course, the explicit cast necessary to create an invariant C in the first place is a bit ugly. Maybe we need "inew" to make new invariant objects?Couldn't the compiler insert the cast based on the declaration?
Apr 09 2008
Steven Schveighoffer wrote:"Georg Wrede" wroteMy mistake.Janice Caron wrote:This cannot be guaranteed by the compiler. It is depending on you to ensure that the object stays invariant. From http://www.digitalmars.com/d/2.0/const3.html "The second way (to create invariant data) is to cast data to invariant. When doing so, it is up to the programmer to ensure that no other mutable references to the same data exist."On 09/04/2008, Denton Cockburn wrote:It should be illegal to cast objects invariant unless the compiler can guarantee the invariantness. So the cast here should produce an error.No, because objects created by new are not necessarily transitively unique. For example class C { int * p; this() { p = &someGlobalVariable; } } invariant C c = cast(invariant) new C; someGlobalVariable = 1; Whoops! c just changed! The explicit cast makes it the programmer's fault, not the compiler's! Come to think of it, "inew" would suffer the exact same problem, so it doesn't solve anything. Looks like there's no easy way to make an invariant class instance.Of course, the explicit cast necessary to create an invariant C in the first place is a bit ugly. Maybe we need "inew" to make new invariant objects?Couldn't the compiler insert the cast based on the declaration?
Apr 09 2008
Lionello Lunesu wrote:One thing that I like is that suddenly C++ "const &" construction can be standardized and made safe at the same time! Because of invariant, "in" can safely pass the arguments by ref, instead of by value.And the compiler can make the decision for you as to which would be more efficient on your architecture. Happiness all around.
Apr 09 2008