Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use pure in D 2.0

While playing around with D 2.0 I found the following problem:

Example 1:

pure string[] run1()
{
   string[] msg;
   msg ~= "Test";
   msg ~= "this.";
   return msg;
}

This compiles and works as expected.

When I try to wrap the string array in a class I find I can not get this to work:

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 code will not compile because the addMsg function is impure. I can not make that function pure since it alters the TestPure object. Am i missing something? Or is this a limitation?

The following does compile:

pure TestPure run3()
{
    TestPure t = new TestPure();
    t.msg ~= "Test";
    t.msg ~= "this.";
    return t;
}

Would the ~= operator not been implemented as a impure function of the msg array? How come the compiler does not complain about that in the run1 function?

like image 914
Jeroen Dirks Avatar asked Jun 17 '09 18:06

Jeroen Dirks


3 Answers

Since v2.050, D relaxed the definition of pure to accept the so called "weakly pure" functions too. This refers to functions that "do not read or write any global mutable state". Weakly pure functions are not the same as pure functions in the functional language sense. The only relation is that they makes real pure functions, a.k.a. "strongly pure" functions able call the weak ones, like OP's example.

With this, addMsg can be marked as (weakly) pure, since only the local variable this.msg is altered:

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

and of course, now you can use the (strongly) pure function run2 with no modification.

pure TestPure run2()
{
   TestPure t = new TestPure();
   t.addMsg("Test");
   t.addMsg("this.");
   return t;
}
like image 161
kennytm Avatar answered Nov 19 '22 01:11

kennytm


Others have already pointed out that addMsg is not pure and cannot be pure because it mutates the state of the object.

The only way to make it pure is to encapsulate the changes you're making. The easiest way to do this is via return mutation, and there are two ways to implement this.

Firstly, you could do it like this:

class TestPure
{
    string[] msg;
    pure TestPure addMsg(string s)
    {
        auto r = new TestPure;
        r.msg = this.msg.dup;
        r.msg ~= s;
        return r;
    }
}

You need to copy the previous array because inside a pure function, the this reference is actually const. Note that you could do the copy better by allocating a new array of the final size and then copying the elements in yourself. You would use this function like so:

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

This way, the mutation is confined to each pure function with changes passed out via return values.

An alternate way of writing TestPure would be to make the members const and do all the mutation before passing it to the constructor:

class TestPure
{
    const(string[]) msg;
    this()
    {
        msg = null;
    }
    this(const(string[]) msg)
    {
        this.msg = msg;
    }
    pure TestPure addMsg(string s)
    {
        return new TestPure(this.msg ~ s);
    }
}

Hope that helps.

like image 25
DK. Avatar answered Nov 19 '22 03:11

DK.


Please review the definition of pure functions:

  • http://en.wikipedia.org/wiki/Pure_function
  • http://www.digitalmars.com/d/2.0/function.html#pure-functions

Pure functions are functions that produce the same result for the same arguments. To that end, a pure function:

  • has parameters that are all invariant or are implicitly convertible to invariant
  • does not read or write any global mutable state

One of the effects of using pure functions is that they can be safely parallelized. However, it's not safe to execute several instances of your function in parallel, because they could both modify the class instance simultaneously, causing a synchronization problem.

like image 27
Vladimir Panteleev Avatar answered Nov 19 '22 03:11

Vladimir Panteleev