April 09, 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".)
April 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.
April 09, 2008
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.

April 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 be

Also I screwed up. Yes, that is legal D. My apologies again. It means as above, plus the function is also pure.
April 09, 2008
"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


April 09, 2008
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
  }
}
April 09, 2008
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*.
April 09, 2008
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.
April 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?




April 09, 2008
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