www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - I just got it! (invariant/const)

reply "Lionello Lunesu" <lionello lunesu.remove.com> writes:
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
next sibling parent reply Jason House <jason.james.house gmail.com> writes:
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
parent reply "Janice Caron" <caron800 googlemail.com> writes:
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
next sibling parent reply Jason House <jason.james.house gmail.com> writes:
Janice Caron Wrote:

 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. [example showing misinterpretation of what I'm talking about]
Consider this example: class D{ void invMemberFunc() invariant; // not pure } class C{ int f(invariant D) invariant pure{ D.invMemberFunc(); // illegal } }
Apr 09 2008
next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
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
parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
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:
  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?
I think it is because invMemberFunc is invariant. Many read this as "will not change anything", even though it is not what it means. -- Simen
Apr 09 2008
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Simen Kjaeraas" wrote
 On Wed, 09 Apr 2008 15:40:37 +0200, Janice Caron  wrote:

 On 09/04/2008, Jason House  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?
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 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. -Steve
Apr 09 2008
prev sibling next sibling parent Jason House <jason.james.house gmail.com> writes:
Simen Kjaeraas Wrote:

 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:
  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?
I think it is because invMemberFunc is invariant. Many read this as "will not change anything", even though it is not what it means. -- Simen
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.
Apr 09 2008
prev sibling parent reply Georg Wrede <georg nospam.org> writes:
Simen Kjaeraas wrote:
 On Wed, 09 Apr 2008 15:40:37 +0200, Janice Caron wrote:
 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 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.
Apr 09 2008
parent reply Jason House <jason.james.house gmail.com> writes:
Georg Wrede Wrote:

 Simen Kjaeraas wrote:
 On Wed, 09 Apr 2008 15:40:37 +0200, Janice Caron wrote:
 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 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.
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.
Apr 09 2008
parent reply "Lionello Lunesu" <lionello lunesu.remove.com> writes:
"Jason House" <jason.james.house gmail.com> wrote in message 
news:ftiuj8$mob$1 digitalmars.com...
 Georg Wrede Wrote:

 Simen Kjaeraas wrote:
 On Wed, 09 Apr 2008 15:40:37 +0200, Janice Caron wrote:
 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 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.
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.
I agree.. And one less keyword, which, it seems, is a goal in itself. L.
Apr 09 2008
parent reply Georg Wrede <georg nospam.org> writes:
Lionello Lunesu wrote:
 
 "Jason House" <jason.james.house gmail.com> wrote in message 
 news:ftiuj8$mob$1 digitalmars.com...
 
 Georg Wrede Wrote:
 Simen Kjaeraas wrote:
 On Wed, 09 Apr 2008 15:40:37 +0200, Janice Caron wrote:
 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 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.
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.
I agree.. And one less keyword, which, it seems, is a goal in itself.
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'".
Apr 10 2008
parent Jason House <jason.james.house gmail.com> writes:
Georg Wrede Wrote:

 Lionello Lunesu wrote:
 
 "Jason House" <jason.james.house gmail.com> wrote in message 
 news:ftiuj8$mob$1 digitalmars.com...
 
 Georg Wrede Wrote:
 Simen Kjaeraas wrote:
 On Wed, 09 Apr 2008 15:40:37 +0200, Janice Caron wrote:
 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 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.
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.
I agree.. And one less keyword, which, it seems, is a goal in itself.
I wasn't thinking of changing D,
Right, but I am ;)
 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
prev sibling next sibling parent reply Georg Wrede <georg nospam.org> writes:
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
next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
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
next sibling parent reply Georg Wrede <georg nospam.org> writes:
Joining Janice's two replies.

Janice Caron wrote:
 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.
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.
Ok.
 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)
Right.
 So it means that "this" is invariant, and that d is invariant.
To be precise, it /requires/ that "this" is invariant, right?
   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.
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.
 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.
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.
 Finally, Since invMemberFunc is not pure, then using it in f should
 really be illegal.
Yes
Good.
 (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.
They 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*.
Apr 09 2008
parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 09/04/2008, Georg Wrede <georg nospam.org> wrote:
    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?
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) { ... }
  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.
 Well, they can be optimised away, at least.
They can be flatly removed!
That's what "optimised away" means. :-)
   - 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
next sibling parent reply Jason House <jason.james.house gmail.com> writes:
Janice Caron Wrote:

 On 09/04/2008, Georg Wrede <georg nospam.org> wrote:
  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. :-)
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
parent reply "Lionello Lunesu" <lionello lunesu.remove.com> writes:
"Jason House" <jason.james.house gmail.com> wrote in message 
news:ftislk$3m0$1 digitalmars.com...
 Janice Caron Wrote:

 On 09/04/2008, Georg Wrede <georg nospam.org> wrote:
  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. :-)
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.
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.pdf
Apr 09 2008
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Lionello Lunesu wrote:
 
 "Jason House" <jason.james.house gmail.com> wrote in message 
 news:ftislk$3m0$1 digitalmars.com...
 Janice Caron Wrote:

 On 09/04/2008, Georg Wrede <georg nospam.org> wrote:
  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. :-)
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.
Why? If pure-ness (uh, purity?) can be checked, it can also be deducted (*), so the compiler could optimize/complain in either case.
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. --bb
Apr 09 2008
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 10/04/2008, Lionello Lunesu <lionello lunesu.remove.com> wrote:
 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.
No I did not. That was Jason House. Please be careful with your level of nesting!
Apr 09 2008
parent "Lionello Lunesu" <lionello lunesu.remove.com> writes:
"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:
 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.
No I did not. That was Jason House. Please be careful with your level of nesting!
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.
Apr 10 2008
prev sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
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
parent reply "Lionello Lunesu" <lionello lunesu.remove.com> writes:
"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:
  Why? If pure-ness (uh, purity?) can be checked, it can also be deducted
"purity" is the right word. (But "deducted" should be "deduced" :-))
:-) thanks.
 (*), 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.
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.
 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
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Lionello Lunesu wrote:
 
 "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:
  Why? If pure-ness (uh, purity?) can be checked, it can also be deducted
"purity" is the right word. (But "deducted" should be "deduced" :-))
:-) thanks.
 (*), 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.
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.
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).
Apr 10 2008
prev sibling parent Georg Wrede <georg nospam.org> writes:
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.
Well, they can be optimised away, at least.
They can be flatly removed!
That's what "optimised away" means. :-)
:-)
  - 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. :-)
In the same manner as unreachable code is an error with many compilers, this might be polite. But, I do believe the template issue!
Apr 09 2008
prev sibling parent reply guslay <guslay gmail.com> writes:
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
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
guslay wrote:
 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?
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. --bb
Apr 09 2008
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"guslay" wrote
 Janice Caron Wrote:

 On 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?
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 } -Steve
Apr 09 2008
next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
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
prev sibling next sibling parent reply guslay <guslay gmail.com> writes:
Steven Schveighoffer Wrote:

 "guslay" wrote
 Janice Caron Wrote:

 On 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?
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 } -Steve
c.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.
Apr 09 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"guslay" wrote
 Steven Schveighoffer Wrote:

 "guslay" wrote
 Janice Caron Wrote:

 On 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?
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 } -Steve
c.getX() is equivalent to " int getX( ref C this ) ", so its not pure.
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
Apr 09 2008
next sibling parent guslay <guslay gmail.com> writes:
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.
 
 -Steve 
 
I can't think of a counter-example, you might be correct.
Apr 09 2008
prev sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
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
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Janice Caron" wrote
 On 09/04/2008, Steven Schveighoffer 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
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.
 (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
parent reply "Janice Caron" <caron800 googlemail.com> writes:
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
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Janice Caron" wrote
 On 09/04/2008, Steven Schveighoffer 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.
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. -Steve
Apr 09 2008
prev sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
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
parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
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
parent reply "Janice Caron" <caron800 googlemail.com> writes:
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
parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
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:
  class foo
  {
         invariant int bar;

         this()
         {
                 bar =3D 4;
         }
  }

  This works under DMD 2.012.
That's interesting. I did not know that.
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? -- Simen
Apr 10 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 09/04/2008, guslay <guslay gmail.com> wrote:
 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 { ... }
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.
Apr 09 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
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 be
Also I screwed up. Yes, that is legal D. My apologies again. It means as above, plus the function is also pure.
Apr 09 2008
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Georg Wrede" wrote
 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)).
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.
 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,

 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 } }
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...
 (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
next sibling parent reply Georg Wrede <georg nospam.org> writes:
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
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Georg Wrede" wrote
 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.
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... -Steve
Apr 09 2008
parent Georg Wrede <georg nospam.org> writes:
Steven Schveighoffer wrote:
 "Georg Wrede" wrote
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.
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?
Well, 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.
 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
prev sibling parent Georg Wrede <georg nospam.org> writes:
Steven Schveighoffer wrote:
 "Georg Wrede" wrote
 
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)).
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.
Ok. 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.
 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.
 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:
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.
 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".
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 } }
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.
Agreed.
(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.
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.
Apr 09 2008
prev sibling parent Jason House <jason.james.house gmail.com> writes:
Jason House Wrote:

 Janice Caron Wrote:
 
 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. [example showing misinterpretation of what I'm talking about]
Consider this example:
[Corrected example - Thanks george] class D{ int invMemberFunc() invariant; // not pure } class C{ int f(invariant D) invariant pure{ return D.invMemberFunc(); // illegal } }
Apr 09 2008
prev sibling parent reply Denton Cockburn <diboss hotmail.com> writes:
On Wed, 09 Apr 2008 13:50:59 +0100, Janice Caron wrote:

 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?
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.
Apr 09 2008
parent reply "Janice Caron" <caron800 googlemail.com> writes:
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
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Janice Caron" wrote
 On 09/04/2008, Denton Cockburn 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.
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
Apr 09 2008
prev sibling parent reply Georg Wrede <georg nospam.org> writes:
Janice Caron wrote:
 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.
It should be illegal to cast objects invariant unless the compiler can guarantee the invariantness. So the cast here should produce an error.
Apr 09 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Georg Wrede" wrote
 Janice Caron wrote:
 On 09/04/2008, Denton Cockburn 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.
It should be illegal to cast objects invariant unless the compiler can guarantee the invariantness. So the cast here should produce an error.
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." -Steve
Apr 09 2008
parent Georg Wrede <georg nospam.org> writes:
Steven Schveighoffer wrote:
 "Georg Wrede" wrote
 
Janice Caron wrote:

On 09/04/2008, Denton Cockburn 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.
It should be illegal to cast objects invariant unless the compiler can guarantee the invariantness. So the cast here should produce an error.
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."
My mistake.
Apr 09 2008
prev sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
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