www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - [SO] Modifing local class instance in pure function

reply Jesse Phillips <myselfhere mailinator.com> writes:
http://stackoverflow.com/questions/1008803/how-to-use-pure-in-d-2-0

class TestPure
{
string[] msg;
void addMsg( string s )
{
       msg ~= s;
   }
};

pure TestPure run2()
{
   TestPure t = new TestPure();
   t.addMsg("Test");
   t.addMsg("this.");
   return t;
}

This doesn't work and the answer from CyberShodow is that you could thread
inside the pure function modifying the class value at the same time. Isn't that
still an issue if TestPure was change to an int?
Jun 17 2009
parent reply div0 <div0 users.sourceforge.net> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Jesse Phillips wrote:
 http://stackoverflow.com/questions/1008803/how-to-use-pure-in-d-2-0
 
 class TestPure
 {
 string[] msg;
 void addMsg( string s )
 {
        msg ~= s;
    }
 };
 
 pure TestPure run2()
 {
    TestPure t = new TestPure();
    t.addMsg("Test");
    t.addMsg("this.");
    return t;
 }
 
 This doesn't work and the answer from CyberShodow is that you could thread
inside the pure function modifying the class value at the same time. Isn't that
still an issue if TestPure was change to an int?
Pure functions are not allowed to alter global state. That's what you doing when you create a new object. http://en.wikipedia.org/wiki/Functional_programming - -- My enormous talent is exceeded only by my outrageous laziness. http://www.ssTk.co.uk -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (MingW32) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFKOrzET9LetA9XoXwRAr7yAJ95tUQ5E6blpFHmnRCSaAcYdN1wCgCeLmZi Bg8FdF/yxx3iR4LS5T+NXsE= =0WQ0 -----END PGP SIGNATURE-----
Jun 18 2009
next sibling parent BCS <ao pathlink.com> writes:
Reply to div0,

 Pure functions are not allowed to alter global state. That's what you
 doing when you create a new object.
 
 http://en.wikipedia.org/wiki/Functional_programming
 
Based on that, cons makes lisp not functional.
Jun 18 2009
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 18 Jun 2009 18:16:36 -0400, div0 <div0 users.sourceforge.net>  
wrote:

 Pure functions are not allowed to alter global state.
 That's what you doing when you create a new object.

 http://en.wikipedia.org/wiki/Functional_programming
I once thought as you do. You are wrong. Memory allocation is a necessary function, and although it is inherently unpure, it must be allowed in pure functions. The problem is that the function boundary is where purity is enforced, not inside. For instance, basic programming 101, split common tasks into functions: int[] f() { int[] x = new int[5]; foreach(i, ref n; x) n = i; return x; } int[] g() { int[] x = new int[6]; foreach(i, ref n; x) n = i; return x; } f and g can be pure, because they do not have side effects. However, consider: void fill(int[] x) { foreach(i, ref n; x) n = i; } int[] f() { int[] x = new int[5]; fill(x); return x; } Note that f is still pure, and fill is contextually pure, since in f's context, it is not altering external state. However, we cannot mark fill as pure, because when called from a non-pure function, it could alter external state. Therefore, fill cannot be called from f if f is pure. This is similar to calling a method on an object: C c = new C; c.method(); The method call translates to: C.method(c); The compiler cannot tell whether c is external state or not, just like it cannot tell that fill() can be contextually pure. So it is not allowed. I'm not sure what the solution is. Something like 'unique' which would ensure that exclusive ownership of an object passes to the method would possibly allow passing mutable state into a pure function. I'm unsure if there are any plans for something like this. -Steve
Jun 18 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:


int[] f()
{
    int[] x = new int[5];
    foreach(i, ref n; x) n = i;
    return x;
}

int[] g()
{
    int[] x = new int[6];
    foreach(i, ref n; x) n = i;
    return x;
}

f and g can be pure, because they do not have side effects.  However,  
consider:

void fill(int[] x)
{
    foreach(i, ref n; x) n = i;
}

int[] f()
{
    int[] x = new int[5];
    fill(x);
    return x;
}

[...]
I'm not sure what the solution is.  Something like 'unique' which would ensure
that exclusive ownership of an object passes to the method would possibly allow
passing mutable state into a pure function.  I'm unsure if there are any plans
for something like this.
<

Probably there are ways to solve the situation, using unique or something else,
but I think that way leads to C++-style madness (see the ridiculously complex
lambdas of the last C++, they look like a joke to me. Those designers have
missed the point that the essential purpose of a lambda as used in mostly
imperative languages is to simplify code and the like, so having one hundred
optional features is bad). Keeping pure functions simple is the key to allow
their optimizations.

To solve your problem you can change your function fill() to restore its purity
(code untested):

pure void range(int n) {
    int[] arr = new int[n];
    foreach (i, ref el; arr)
        el = i;
    return arr;
}

pure int[] f() {
    return range(5);
}

pure int[] g() {
    return range(6);
}

Or you can just accept to not mark fill(), f() and g() as pure.

Bye,
bearophile
Jun 19 2009
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
bearophile Wrote:


 Probably there are ways to solve the situation, using unique or something
else, but I think that way leads to C++-style madness (see the ridiculously
complex lambdas of the last C++, they look like a joke to me. Those designers
have missed the point that the essential purpose of a lambda as used in mostly
imperative languages is to simplify code and the like, so having one hundred
optional features is bad). Keeping pure functions simple is the key to allow
their optimizations.
Such complexity is necessary when you are trying to encapsulate functional benefits with imperative style. I happen to think we need unique for const/multithreading anyways, why not extend that to help pure functions be modularized?
 
 To solve your problem you can change your function fill() to restore its
purity (code untested):
Yes, I thought of that solution. I intentionally did not write the code that way to illustrate the point I was trying to make. I'm expecting the reader to envision larger examples where it's not as convenient to change the modularization point, especially with previously existing functions that are hard to rewrite. Consider that you can't sort an array you create in a pure function without rewriting sort as a pure function!
 Or you can just accept to not mark fill(), f() and g() as pure.
fill cannot be pure, because it is only contextually pure (pure when called from a pure function). I'm not saying there is an easy solution, but the problems are not completely solved by the current methodology. If they can be solved, I think D will be in a class of it's own. -Steve
Jun 19 2009
prev sibling parent reply div0 <div0 users.sourceforge.net> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Steven Schveighoffer wrote:
 On Thu, 18 Jun 2009 18:16:36 -0400, div0 <div0 users.sourceforge.net>
 wrote:
 
 Pure functions are not allowed to alter global state.
 That's what you doing when you create a new object.

 http://en.wikipedia.org/wiki/Functional_programming
I once thought as you do. You are wrong. Memory allocation is a necessary function, and although it is inherently unpure, it must be allowed in pure functions. The problem is that the function boundary is where purity is enforced, not inside. For instance, basic programming 101, split common tasks into functions: int[] f() { int[] x = new int[5]; foreach(i, ref n; x) n = i; return x; } int[] g() { int[] x = new int[6]; foreach(i, ref n; x) n = i; return x; } f and g can be pure, because they do not have side effects. However, consider: void fill(int[] x) { foreach(i, ref n; x) n = i; } int[] f() { int[] x = new int[5]; fill(x); return x; } Note that f is still pure, and fill is contextually pure, since in f's context, it is not altering external state. However, we cannot mark fill as pure, because when called from a non-pure function, it could alter external state. Therefore, fill cannot be called from f if f is pure. This is similar to calling a method on an object: C c = new C; c.method(); The method call translates to: C.method(c); The compiler cannot tell whether c is external state or not, just like it cannot tell that fill() can be contextually pure. So it is not allowed. I'm not sure what the solution is. Something like 'unique' which would ensure that exclusive ownership of an object passes to the method would possibly allow passing mutable state into a pure function. I'm unsure if there are any plans for something like this. -Steve
Nuts I missed the main point of what I meant to say. Though I can see the argument against the heap being global state is necessary or you won't be able to do much with pure functions. In your example you are returning values types, not objects as in the original question. Objects are defined to have an identity, so by returning a new object from the function you are not getting the same result for the same inputs. Does D relax the definition of pure to say a function may return an equivalent object? - -- My enormous talent is exceeded only by my outrageous laziness. http://www.ssTk.co.uk -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (MingW32) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFKO7EwT9LetA9XoXwRAkyiAJ0fUG20aU1IJSEa62yOCOTubvsmRgCgooGF CXT3z2MqmhA+4wx8UW0Dyqc= =uhBi -----END PGP SIGNATURE-----
Jun 19 2009
next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
div0 Wrote:
 In your example you are returning values types, not objects as in the
 original question.
Arrays are not value types. Substitute int[] x with a pointer, same argument applies. -Steve
Jun 19 2009
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
div0:
 Objects are defined to have an identity, so by returning a new object
 from the function you are not getting the same result for the same inputs.
 Does D relax the definition of pure to say a function may return an
 equivalent object?
Interesting question, I have tried the following code with D2: import std.stdio: writeln; class C {} pure C genc() { return new C; } void main() { writeln(genc() is genc()); } The generated asm shows that pure of genc is ignored: With "pure": main: L0: push EBX mov EAX,offset FLAT:_D5temp31C7__ClassZ push EAX call near ptr __d_newclass add ESP,4 mov ECX,offset FLAT:_D5temp31C7__ClassZ push EAX sub ESP,4 push ECX call near ptr __d_newclass add ESP,4 add ESP,4 mov EDX,EAX mov EBX,1 pop EAX cmp EAX,EDX je L32 xor EBX,EBX L32: mov EAX,offset FLAT:_D3std5stdio6stdoutS3std5stdio4File push EBX push 0Ah call near ptr _D3std5stdio4File14__T5writeTbTaZ5writeMFbaZv xor EAX,EAX pop EBX ret Without "pure": main: L0: push EBX mov EAX,offset FLAT:_D5temp31C7__ClassZ push EAX call near ptr __d_newclass add ESP,4 mov ECX,offset FLAT:_D5temp31C7__ClassZ push EAX sub ESP,4 push ECX call near ptr __d_newclass add ESP,4 add ESP,4 mov EDX,EAX mov EBX,1 pop EAX cmp EAX,EDX je L32 xor EBX,EBX L32: mov EAX,offset FLAT:_D3std5stdio6stdoutS3std5stdio4File push EBX push 0Ah call near ptr _D3std5stdio4File14__T5writeTbTaZ5writeMFbaZv xor EAX,EAX pop EBX ret I don't know what's happening under the hood into dmd here, Don may give a better answer. Bye, bearophile
Jun 19 2009