pure or not pure?

Steven Schveighoffer schveiguy at yahoo.com
Thu Apr 10 06:26:20 PDT 2008


"Janice Caron" wrote
> On 09/04/2008, Steven Schveighoffer wrote:
>> So how is new char[] a pure function?
>
> new is special.
>
>>  And why can't I 'wrap' new?  For
>>  instance, if I have a function:
>>
>>  int[] createIncreasingSequence(int s, int e, int step){...}
>>
>>  which creates an array of ints that start at s and end at e, increasing 
>> by
>>  step each time, why can't I make that pure?
>
> I reckon you can, but you got the return type wrong.
>
>    invariant(int)[] createIncreasingSequence(int s, int e, int step){...}
>    {
>        auto array = new int[(e-s)/step];
>        foreach(ref a;array)
>        {
>            a = e;
>            e += step;
>        }
>        return assumeUnique!(array);
>    }

This doesn't work for what I'm asking.  I'll add more detail to the example. 
You have two functions f, and g:

pure int f()
{
    int[] x = new int[5];
    for(int i = 0; i < x.length; i++)
       x[i] = 1 + i;

    /* do stuff */
    return result;
}

pure int g()
{
    int[] y = new int[15];
    for(int i = 0; i < y.length; y++)
        y[i] = 10 + (i * 3);

    /* do stuff */
    return result;
}

Now, I realize I have very similar code that initializes the int arrays, so 
I want to put that into a function that creates and initializes an array 
with the given parameters:

int[] createIncreasingSequence(int length, int start, int step)
{
    int[] result = new int[length];
    for(int i = 0; i < result.length; i++)
        result[i] = start + (i * step);
    return result;
}

Now, I think we all agree that createIncreasingSequence is as pure as 
calling new, right?  That is, it doesn't affect any global state (except for 
allocating heap data) and will return an equivalent array every time the 
same arguments are given.  But if I can't declare it as 'pure', then I can't 
call it from f and g:

pure int f()
{
   int[] x = createIncreasingSequence(5, 1, 1);

   /* do stuff */
   return result;
}

pure int g()
{
   int[] y = createIncreasingSequence(15, 10, 3);

   /* do stuff */
   return result;
}

So should there be a way to define createIncreasingSequence such that I can 
call it from a pure function?  Or do I have to re-implement it in every pure 
function I use?

Not having the ability to pass around mutable heap data to and from pure 
functions is going to limit severely the usefulness of pure functions.

>> Surely there is some possible need for having run-time initialized 
>> invariant
>>  variables...
>
> You can do that. Walter confirmed that in response to a previous example I 
> gave.
>
>   import std.date;
>
>   void main()
>   {
>       invariant s = toString(getUTCtime());
>
>       pure string f()
>       {
>           return s;
>       }
>
>       writefln(s);
>   }
>
> Walter says f is pure. Ironically, the program will give a different
> output each time it's run, but aparently purity only has to be global,
> not universal.

This is exactly what I want to do, But I want to do it inside a class scope, 
not in a function scope.  I think someone in another thread already 
confirmed that this is possible in DMD 2.012, so my question is already 
answered.

>>  > I think this is a /necessary/ restriction, because otherwise the
>>  > following could not be typechecked:
>>  >
>>  >    class C
>>  >    {
>>  >        invariant int * p;
>>  >
>>  >        this(int * q)
>>  >        {
>>  >            p = q; // Big no no!
>>  >        }
>>  >    }
>>
>>
>> Why would that compile?
>
> Thankfully, it doesn't. But you were suggesting it should be possible
> to initialize member variables which were declared invariant inside a
> constructor, so I was demonstrating why this would not be a good idea.

Like I said, this is already possible.

>>  If it did, then so would this:
>>
>>  this(int *q) invariant
>>  {
>>     p = q;
>>  }
>
> Not so. Invariant constructors have different typechecking rules.
>
> Member variable p initially has type __raw(int *), which means it can
> only be assigned with an invariant(int *), not with an int *.

I would guess that this is a requirement for invariant class members as 
well.

>>  I think invariant members that are run-time initialized can be done, as
>>  during the constructor, nothing has access to the 'this' pointer.
>
> "this" is certainly available during a constructor.

I would guess that you cannot pass 'this' to an external entity before 
initializing the invariant variables (or before initializing mutable 
variables in an invariant constructor).  This should be statically 
verifiable.

-Steve 





More information about the Digitalmars-d mailing list