I just got it! (invariant/const)

Georg Wrede georg at nospam.org
Wed Apr 9 11:21:15 PDT 2008


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.



More information about the Digitalmars-d mailing list