www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - A question about purity

reply bearophile <bearophileHUGS lycos.com> writes:
A D2 program:


struct Foo {
    int x;
    static int y;
    pure void foo1() {
        this.x++;
    }
    static pure void foo2() {
        Foo.y++; // line 8, error
    }
}
void main() {}


With DMD 2.055 it gives at compile-time:
test.d(8): Error: pure function 'foo2' cannot access mutable static data 'y'

If a (weakly?) pure method foo1 is allowed to change instance attributes, do
you know why a static (weakly?) pure method can't change static attributes?
What's the purity difference between foo1 and foo2? I cant' see a lot of
difference.

Bye,
bearophile
Sep 12 2011
next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, September 12, 2011 15:24 bearophile wrote:
 A D2 program:
 
 
 struct Foo {
 int x;
 static int y;
 pure void foo1() {
 this.x++;
 }
 static pure void foo2() {
 Foo.y++; // line 8, error
 }
 }
 void main() {}
 
 
 With DMD 2.055 it gives at compile-time:
 test.d(8): Error: pure function 'foo2' cannot access mutable static data
 'y'
 
 If a (weakly?) pure method foo1 is allowed to change instance attributes,
 do you know why a static (weakly?) pure method can't change static
 attributes? What's the purity difference between foo1 and foo2? I cant'
 see a lot of difference.

In foo1, this is one of its arguments - albeit an invisible one. In foo2, y is not one of its arguments. If a strongly pure function allocates a Foo, it can know that when it calls foo1 that nothing outside of that Foo is altered by the call. All of the arguments to foo1 are encapsulated in the strongly pure function and affect nothing outside of it. However, Foo.y is not encapsulated by a strongly pure function at all. Other functions can alter alter it. So, it breaks purity. - Jonathan M Davis
Sep 12 2011
parent reply bearophile <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 However, Foo.y is not encapsulated 
 by a strongly pure function at all. Other functions can alter alter it. So, it 
 breaks purity.

Thank you for your explanation :-) So, let's change the situation a bit. If the struct Foo is the only thing present in a module (to avoid someone to touch its private members), and the y field is "private static" only foo2 is able to touch it. In this case isn't foo2 weakly pure? struct Foo { private static int y; static pure void foo2() { Foo.y++; } } void main() {} Bye, bearophile
Sep 13 2011
next sibling parent travert phare.normalesup.org (Christophe) writes:
bearophile , dans le message (digitalmars.D.learn:29490), a écrit :
 Jonathan M Davis:
 
 However, Foo.y is not encapsulated 
 by a strongly pure function at all. Other functions can alter alter it. So, it 
 breaks purity.

Thank you for your explanation :-) So, let's change the situation a bit. If the struct Foo is the only thing present in a module (to avoid someone to touch its private members), and the y field is "private static" only foo2 is able to touch it. In this case isn't foo2 weakly pure?

No, because you change a variable that is global, even if it is private. In foo.foo1(), foo is changed, but it can be considered as an argument and a part of the result of foo1(). You cannot say this for Foo.y. Here is an example to illustrate what was meant with "encapsulating with a pure function" : struct Foo { int x; pure int foo1() { return x++; } private static int y; static pure int foo2() { return Foo.y++; // not pure: a global variable changes } } pure int test1(Foo foo) { auto a = foo.foo1(); // nothing changes outside test1: only the local // copy of foo changes. return a; } pure int test2() { auto b = Foo.foo2(); // error: Foo.y changes return b; } void main() { Foo foo; auto a = test1(foo); // only a copy of foo is changed. auto b = test1(foo); assert(a==b); // ok auto c = test2(); // error: Foo.y changes auto d = test2(); assert(c==d); // fails if test2 was indeed run twice. }
Sep 13 2011
prev sibling parent Kagamin <spam here.lot> writes:
bearophile Wrote:

 So, let's change the situation a bit. If the struct Foo is the only thing
present in a module (to avoid someone to touch its private members), and the y
field is "private static" only foo2 is able to touch it. In this case isn't
foo2 weakly pure?

Weakly pure function causes side effect with a lifetime limited to enclosing pure function, changing static data causes side effect which escapes enclosing pure function.
Sep 13 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, September 13, 2011 07:43:55 bearophile wrote:
 Jonathan M Davis:
 However, Foo.y is not encapsulated
 by a strongly pure function at all. Other functions can alter alter it.
 So, it breaks purity.

Thank you for your explanation :-) So, let's change the situation a bit. If the struct Foo is the only thing present in a module (to avoid someone to touch its private members), and the y field is "private static" only foo2 is able to touch it. In this case isn't foo2 weakly pure? struct Foo { private static int y; static pure void foo2() { Foo.y++; } } void main() {}

Pure functions cannot access mutable static variables. End of story. _That_ is the definition of pure in D. The terms strong and weakly pure relate only to whether a particular call to a pure function can be optimized out, and Don (who created the terms) would like to see the terms die out. Yes, in this particular situation, if you treat everything in the program as an argument to the function (which is completely impractical), then you know that calling foo2 won't break purity. But that's just way too complicated. The compiler doesn't work that way and never will. And there would be no point in making it work that way even if it were feasible, because this example is completely contrived. It's just not going to happen in real life. - Jonathan M Davis
Sep 13 2011
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 13 Sep 2011 07:43:55 -0400, bearophile <bearophileHUGS lycos.com>  
wrote:

 Jonathan M Davis:

 However, Foo.y is not encapsulated
 by a strongly pure function at all. Other functions can alter alter it.  
 So, it
 breaks purity.

Thank you for your explanation :-) So, let's change the situation a bit. If the struct Foo is the only thing present in a module (to avoid someone to touch its private members), and the y field is "private static" only foo2 is able to touch it. In this case isn't foo2 weakly pure? struct Foo { private static int y; static pure void foo2() { Foo.y++; } } void main() {}

weak purity vs. strong purity is determined by the parameters. Strong-pure functions must have all parameters immutable or implicitly convertable to immutable. Since foo2 has no parameters, it will be classified as strong-pure. Yet, pure optimizations cannot be used on it. For example, for a strong-pure function fn, the following calls can be reduced to one call: int i = fn(); int j = fn(); // can be rewritten to int i = j But foo2 cannot be optimized this way, it would change the value of y. What you want to do is consider the private static state of a class/struct to be part of the parameters to such functions, which I think negates a lot of possible optimizations, when those static variables are not used. It makes too broad an assumption. Not to mention, static private variables are accessible from *any* member function, which means any member function of a class/struct which contains private static variables could not be strong-pure. But the real killer: the side effect makes foo2 not composable into strong-pure functions. That is, a strong pure function that calls a weak pure function *does not* become weak pure. However, in this case, it would have to. The whole system breaks down once you start implying global state is a parameter. -Steve
Sep 14 2011