> The wikipedia article on pure functions is weak on its sources.
> The only reference supporting the proposition that a 'pure function'
> can access external _mutable_ state appears to be in relation to the
> gcc 'pure' attribute. That can mean what gcc want it to mean,
> possibly aided by the fact that it has a separate 'const' attribute
> which means what I say 'pure' means except that (i) it seems 'const'
> functions cannot read _immutable_ globals, and (ii) the description
> of the 'const' attribute doesn't seem to say anything about
> non-global external mutable data (although the explanation implies
> that accessing that is disallowed). (Note for other readers: the
> gcc 'const' function attribute has nothing to do with the 'const'
> keyword in C and C++.)
>
> In D at least, the pure keyword precludes reading from or writing to
> any global or static mutable state. That is, I would suggest, the
> word's more common usage. However, D does allow pure functions
> amongst other things to throw exceptions "as a concession to
> practicality".
>
> This perhaps comes back to the point that the language in question
> will decide what is "pure" in the context of whatever compiler
> optimizations it is trying to support by adopting that keyword.
My impression is that "pure" in the sense of "not having side
effects" is more common and more usual in the general programming
community. It aligns with usage in the functional programming
community, where language features are described as "pure" or
"impure" according to whether side-effects may occur. It's
consistent with my memory of learning to distinguish pure Lisp,
"sometimes humorously referred to as poor Lisp" (admittedly not a
very reliable data point). Furthermore the property of being
side-effect free (but possibly accessing state that is otherwise
mutable) is a useful distinction to make, both for operators and
functions, because a set of such operations may be arbitrarily
reordered without changing the meaning, regardless of whether
mutable state is accessed. These items plus the existence of the
term "referentially transparent", which unambiguously has the
stronger meaning, lead me to think it is better to use "pure"
to mean just side-effect free, and "referentially transparent"
for the stronger meaning. In any case it seems best to limit
"pure" to mean no more than one of those two meanings, and not
add another definition which would further confuse the issue.
>>> I think it probably follows, from the C++ specification for
>>> constexpr functions, that they are pure.
>>
>> They might be but don't have to be, depending on the particular
>> argument values:
>>
>> constexpr int
>> foo( int x, int bar( int ) ){
>> return bar( x );
>> }
>>
>> constexpr int
>> pure( int x ){
>> return x;
>> }
>>
>> int
>> impure( int x ){
>> static int z;
>> return x + z++;
>> }
>>
>> constexpr int three = foo( 3, pure );
>>
>> #include <iostream>
>>
>> int
>> main(){
>> const int a = foo( 3, impure );
>> const int b = foo( 3, impure );
>> const int c = foo( 3, impure );
>> std::cout << ' ' << a << ' ' << b << ' ' << c << '\n';
>> }
>
> Kudos: very clever. I am astonished that constexpr functions can take a
> reference to a non-constexpr function as an argument and then call it.
Thank you, I can live for a month on a compliment, as the saying
goes. For myself, I wasn't sure when I tried it that it would
work, but I wasn't surprised when it did; I would have been more
surprised if it didn't.
> The values of 'a', 'b' and 'c' presumably are not computed at compile
> time because the argument passed to 'bar' ('impure') is not constexpr.
Right. In principle they could be in this example, since the
compiler can see all the places 'impure' is used, and figure
things out in advance. But neither g++ nor clang was smart
enough to do so, and certainly it cannot be done in general.
> This does appear to be a hole in the C++ language specification. I can
> sort-of see the justification for it - the argument passed to 'bar'
> might, or might not, be constexpr, and when it is then 'foo' is (as
> required) called at compile time, as when initializing 'three'. But it
> seems perverse.
I don't think of it as a hole at all. A constexpr function is
just a regular function, except with certain restrictions that
guarantee it can be evaluated at compile time, _provided_ certain
things are true of the arguments given. But if the return value
doesn't need to be constexpr, a constexpr function can be used
just like a regular function[*]. I see this quality as a
strength, not a weakness.
[*] Disclaimer: I believe. I find the C++ standard very hard
reading, and in this case I didn't even try to read what it
requires in such cases.