D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 2832 - pure function too pure
Summary: pure function too pure
Status: RESOLVED FIXED
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: x86 Linux
: P2 enhancement
Assignee: No Owner
URL:
Keywords:
: 4630 (view as issue list)
Depends on:
Blocks:
 
Reported: 2009-04-11 18:26 UTC by Andrei Alexandrescu
Modified: 2015-06-09 01:20 UTC (History)
6 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Andrei Alexandrescu 2009-04-11 18:26:27 UTC
pure int fun(int d, int divisor)
{
    if (d < 0)
        d -= divisor - 1;
    return d / divisor;
}

This doesn't compile because d is assumed to be constant. It doesn't have to because it's a private copy of the function. Beware when fixing this - any indirectly-referenced data is not part of function's private state.
Comment 1 Don 2009-07-03 02:17:24 UTC
Comment: Allowing this would mean that nested pure functions would no longer be able to use the parameters from the enclosing function.

pure int fun(int d, int divisor)
{
   int gun() pure { return d+1; } // would be illegal, since 'fun' could modify d.
    return gun() + d / divisor;
}

Which in turn would mean that nested pure functions would become quite useless. So there's a price to pay.
Comment 2 Max Samukha 2009-10-13 03:46:47 UTC
Instead of introducing another inconsistency into the language for the not-so-common case, you could take the opposite route:

pure int fun(int d, int divisor)
{
   immutable c = d;
   int gun() pure { return c + 1; }

   return gun() + d / divisor;
}
Comment 3 Don 2009-10-13 06:06:14 UTC
(In reply to comment #2)
> Instead of introducing another inconsistency into the language for the
> not-so-common case, you could take the opposite route:
> 
> pure int fun(int d, int divisor)
> {
>    immutable c = d;
>    int gun() pure { return c + 1; }
> 
>    return gun() + d / divisor;
> }

I think the existing behaviour -- that you cannot change any of the parameters in a pure function -- is simple and intuitive: pure functions can only modify variables which they created themselves. A rule that pure nested functions can use indirectly-referenced data, but cannot use parameters which are passed by value, just seems complicated. 
Especially, in the case where a parameter contains a reference to other data, it seems folly to be allowed to change part of the parameter, but not all of it.
Comment 4 Max Samukha 2009-10-13 08:44:23 UTC
> I think the existing behaviour -- that you cannot change any of the parameters
> in a pure function -- is simple and intuitive: pure functions can only modify
> variables which they created themselves.

But the passed-by-value parts of the arguments are copied and consequently can be qualified as "variables which they created themselves".

> A rule that pure nested functions can
> use indirectly-referenced data, but cannot use parameters which are passed by
> value, just seems complicated.

I don't think it is too complicated. It can be trivially done like this:

pure int foo(in int d, ...)
{
 // now we should be able to use d in pure nested functions because it is 
 // guaranteed to not change during the function call.
}

> Especially, in the case where a parameter contains a reference to other data,
> it seems folly to be allowed to change part of the parameter, but not all of
> it.

I am not sure. For example, it seems to be fairly intuitive to be able to rebind a string parameter, though changing the referenced part of it is not allowed. I would agree if D's function parameters behaved like aliases to the arguments, but they are more like the function's local variables, which arguments are assigned to.

Now that I am trying to purify some functions (most of which have no nested functions) I need to add the useless temporaries to make them compile :(
Comment 5 Walter Bright 2009-12-11 23:30:21 UTC
This would reintroduce "tail-const" for the parameters, something that we tried hard to make work and failed. I'd rather just leave the parameters const. If you have to make a mutable copy, as in:

  pure int fun(int d, int divisor)
  { auto x = d;
    if (x < 0)
        x -= divisor - 1;
    return x / divisor;
  }

that isn't much of a burden, and the compiler will optimize the extra copy away anyway.
Comment 6 David Simcha 2010-08-15 21:24:26 UTC
*** Issue 4630 has been marked as a duplicate of this issue. ***
Comment 7 Tomash Brechko 2010-09-22 07:39:43 UTC
Note that (with dmd 2.049)

pure int f(int i)
{
  return ++i;
}

doesn't compile, yet

pure auto f(int i)
{
  return ++i;
}

does work (int return type is replaced with auto).

Also http://www.digitalmars.com/d/2.0/function.html section "Pure Functions" gives an example of assignment to parameter,

  i = y;    // ok, reading immutable global state
Comment 8 Tomash Brechko 2010-09-22 08:05:37 UTC
Created separate issue http://d.puremagic.com/issues/show_bug.cgi?id=4915 for "int -> auto", as auto return type escapes pure completely.
Comment 9 Jesse Phillips 2010-11-16 13:19:39 UTC
In the recent update of relaxed purity, this code now compiles. However I believe this is a weakly pure function as a strongly pure function requires its parameters to be immutable.