December 23, 2016
On Thursday, 22 December 2016 at 18:49:02 UTC, Stefan Koch wrote:
> On Thursday, 22 December 2016 at 18:04:51 UTC, Observer wrote:
>
>> (1) Serve as a convenient breakpoint handle in the debugger, perhaps
>>     as a kind of centralized this_cannot_ever_happen() function.
>> (2) conditionally_die(conditions);
>> (3) Sleep for some run-time-computable length of time.
>> (4) Yield the thread so other threads can take execution priority.
>> (5) Yield the entire process so other processes can take execution
>>     priority.
>> (6) Wait for an external trigger (perhaps a hardware interrupt, for
>>     instance).
>> (7) Invoke a pass of garbage collection.
>
>
> A function that does any of that cannot be pure.

You judge too quickly.  (1) is certainly possible:

% cat pure.d
pure void cannot_happen_breakpoint () {
    return;
}

int main () {
    cannot_happen_breakpoint();
    return 0;
}
% dmd --version
DMD64 D Compiler v2.071.0
Copyright (c) 1999-2015 by Digital Mars written by Walter Bright
% dmd pure.d
% nm -pr pure | fgrep cannot
0000000000422a60 T _D4pure24cannot_happen_breakpointFNaZv
% gdb pure
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
...
Reading symbols from pure...(no debugging symbols found)...done.
(gdb) break _D4pure24cannot_happen_breakpointFNaZv
Breakpoint 1 at 0x422a64
(gdb) run
Starting program: /home/anon/dlang/pure
warning: the debug information found in "/lib64/ld-2.19.so" does not match "/lib64/ld-linux-x86-64.so.2" (CRC mismatch).

[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, 0x0000000000422a64 in pure.cannot_happen_breakpoint() ()

With respect to the others, I see in testing that the compiler
objects to a pure function calling an impure function such as
sched_yield().  I was misled in my thinking by the nearly-exclusive
focus on memory mutation in the TDPL discussion of pure functions.

Note to Andrei:  in a revised TDPL, this aspect should be broadened,
to include explicit mention of other sorts of things that a pure
function is not allowed to do.  Yes, I know the text says "a function
is considered pure if returning a result is its only effect", but
from a teaching point of view, that's not enough emphasis to catch
the reader's attention when the entire rest of the discussion is
about mutation.

(Though frankly, I don't know why sched_yield() shouldn't be marked
as pure in core.sys.posix.sched.  It's not like a pure function
cannot be interrupted by a timeslice expiration at the OS level,
so calling sched_yield() ought to open no new doors.)
December 23, 2016
On Friday, 23 December 2016 at 06:53:25 UTC, Observer wrote:
[ ... ]

A pure function MUST NOT mutate any state except what is reachable through it's arguments.
This includes ANY operating system state.

As for your debugger point.
You can break on a pure function as well as on any other.
And, yes that breakpoint can be shifted or invalided by compiler optimizations.
That is why there are debug builds for which the compiler avoids most optimizations.
December 23, 2016
On Friday, December 23, 2016 07:29:49 Stefan Koch via Digitalmars-d wrote:
> On Friday, 23 December 2016 at 06:53:25 UTC, Observer wrote: [ ... ]
>
> A pure function MUST NOT mutate any state except what is
> reachable through it's arguments.
> This includes ANY operating system state.

That's not quite true. For instance, the spec specifically permits pure functions to read and write the floating point exception flags. Also, functions like malloc have been marked with pure, and that technically involves mucking with the OS state, albeit in a very specific and limited way. So, there _are_ a few instances where we allow it. That being said, it's certainly true that _most_ things that would involve the OS state are forbidden from being messed with in pure functions unless the resource in question is accessed via a function parameter. And any exceptions to that rule have to be for a very good reason.

- Jonathan M Davis

December 23, 2016
On Thursday, December 22, 2016 18:04:51 Observer via Digitalmars-d wrote:
> It seems to me that a pure function could have a variety of acceptable side effects which don't modify active memory and that don't work around the type system or necessarily eat significant CPU time, and that you probably don't want to have elided.  Here are some examples.
>
> (1) Serve as a convenient breakpoint handle in the debugger,
> perhaps
>      as a kind of centralized this_cannot_ever_happen() function.

I suppose, but that doesn't really have anything to do with what a purity. Any function could be a point to break in the debugger.

> (2) conditionally_die(conditions);

Depending on what you mean by conditionally die, yes, that's legit. Errors can be thrown from pure functions (even strongly pure functions), and the spec specifically allows a pure function to terminate the program (so calling exit should be legit, though I'm not sure if it's actually marked as pure).

> (3) Sleep for some run-time-computable length of time.

This doesn't really make sense. It _might_ make sense for a weakly pure function to do so via a function argument, but once we're talking strongly pure functions, this doesn't make sense at all. At that point, we're talking functional purity. The same input is supposed to give the exact same output, and the compiler is free to optimize out calls to the pure function when it's called multiple times with the same arguments. So, relying on anything to do with timing simply wouldn't work, and sleeping in a strongly pure function just doesn't make sense. It's questionable as to whether it even makes sense in a weakly pure one. Regardless, right now, it certainly isn't legal, because there is no sleep function marked as pure in druntime.

> (4) Yield the thread so other threads can take execution priority.
>
> (5) Yield the entire process so other processes can take execution
>      priority.

These both have basically the same problem as sleeping. They may no sense for strongly pure functions and questionable sense for weakly pure ones and are not currently allowed in pure functions.

> (6) Wait for an external trigger (perhaps a hardware interrupt,
> for
>      instance).

This definitely does not sound legit. It explicit depends on the OS state, which is the sort of thing that's generally banned in pure functions.

> (7) Invoke a pass of garbage collection.

This, however, would probably be legit for the same reasons that new and malloc are considered pure. Certainly, most of core.memory.GC is either marked as pure or /* FIXME pure */, and given that GC.free is already pure, having GC.collect be pure would make sense.

> My point here is that when considering what functions do, mutating
> memory is only part of the story.  Control of time and other
> resources
> can be a critical part of overall program execution, and you
> don't want
> the compiler assuming it can ignore such aspects.

Conceptually, it makes no sense to be doing any of that sort of thing in a strongly pure function, because at that point, we're really talking functional purity. The same input is supposed to give the same output, and while it's debatable whether timing is part of that, it would have to at least be taking the same actions given the same input. And if the compiler has to worry about how the timing of a pure function affects the program, then it could _never_ elide a call to a pure function, which would defeat one of the main reasons that the feature was introduced in the first place. So, I'm inclined to think that anything with regards to timing has no business in a pure function.

- Jonathan M Davis

December 23, 2016
On Friday, 23 December 2016 at 11:11:09 UTC, Jonathan M Davis wrote:
> Conceptually, it makes no sense to be doing any of that sort of thing in a strongly pure function, because at that point, we're really talking functional purity.

I understand your points.  I had been thinking about purity purely
from the standpoint of external mutation, since that's what the TDPL
discussion emphasizes, and neglected to consider possible compiler
optimizations (hoisting calls out of loops, for instance).  That
sort of thing would definitely mess with all of the examples I had
proposed.
December 23, 2016
On Friday, December 23, 2016 14:12:54 Observer via Digitalmars-d wrote:
> On Friday, 23 December 2016 at 11:11:09 UTC, Jonathan M Davis
>
> wrote:
> > Conceptually, it makes no sense to be doing any of that sort of thing in a strongly pure function, because at that point, we're really talking functional purity.
>
> I understand your points.  I had been thinking about purity purely
> from the standpoint of external mutation, since that's what the
> TDPL
> discussion emphasizes, and neglected to consider possible compiler
> optimizations (hoisting calls out of loops, for instance).  That
> sort of thing would definitely mess with all of the examples I had
> proposed.

Optimization really only comes into play when the function is "strongly" pure so that the compiler can guarantee that the arguments aren't mutated and thus can guarantee that the function will always give the same result for the same arguments, but it was originally one of the primary goals of pure. Once pure was expanded to include "weakly" pure functions that accepted mutable arguments but just didn't access mutable global state, then the benefits of pure expanded considerably without really adding to the optimization opportunities beyond the fact that without relaxing the purity rules, pure functions were almost useless, because having to have all of the parameters be immutable or implicitly convertible to immutable was just too restrictive to be useful in most cases.

But since the whole reason that pure was relaxed was to make it so that more functions could be "strongly" pure, we really don't want to degrade pure so far that we lose out on the original benefits that pure was supposed to provide, much as the fact that a pure function can't access mutable, global state except via its arguments is actually a big benefit in its own right even without the optimizations entering into the mix.

- Jonathan M Davis

December 23, 2016
On Thursday, 22 December 2016 at 20:53:37 UTC, deadalnix wrote:
> On Wednesday, 21 December 2016 at 21:34:04 UTC, Andrei Alexandrescu wrote:
>>> Instead of
>>>   "Any `pure` function that is not strongly pure cannot be memoized."
>>> why not
>>>   "Any `pure` function that is not strongly pure _may not be assumed to
>>> be_ memoizable."
>>
>> Got it. Good point. Will do.
>>
>
> That worse than the current wording.

Yes, and I fixed it a few minutes after: https://forum.dlang.org/post/tnvpmtxcmqiwlmedyiei@forum.dlang.org

December 23, 2016
On 12/23/2016 12:32 PM, Johan Engelen wrote:
> On Thursday, 22 December 2016 at 20:53:37 UTC, deadalnix wrote:
>> On Wednesday, 21 December 2016 at 21:34:04 UTC, Andrei Alexandrescu
>> wrote:
>>>> Instead of
>>>>   "Any `pure` function that is not strongly pure cannot be memoized."
>>>> why not
>>>>   "Any `pure` function that is not strongly pure _may not be assumed to
>>>> be_ memoizable."
>>>
>>> Got it. Good point. Will do.
>>>
>>
>> That worse than the current wording.
>
> Yes, and I fixed it a few minutes after:
> https://forum.dlang.org/post/tnvpmtxcmqiwlmedyiei@forum.dlang.org

Is the situash good now? -- Andrei

December 23, 2016
On Friday, 23 December 2016 at 17:42:40 UTC, Andrei Alexandrescu wrote:
> On 12/23/2016 12:32 PM, Johan Engelen wrote:
>> On Thursday, 22 December 2016 at 20:53:37 UTC, deadalnix wrote:
>>> On Wednesday, 21 December 2016 at 21:34:04 UTC, Andrei Alexandrescu
>>> wrote:
>>>>> Instead of
>>>>>   "Any `pure` function that is not strongly pure cannot be memoized."
>>>>> why not
>>>>>   "Any `pure` function that is not strongly pure _may not be assumed to
>>>>> be_ memoizable."
>>>>
>>>> Got it. Good point. Will do.
>>>>
>>>
>>> That worse than the current wording.
>>
>> Yes, and I fixed it a few minutes after:
>> https://forum.dlang.org/post/tnvpmtxcmqiwlmedyiei@forum.dlang.org
>
> Is the situash good now? -- Andrei

Yeah, with the extra sentences it's clear to (at least) me. The "cannot be assumed" may be read as "can never be assumed"; but it's clarified at the end of the paragraph.

Perhaps I read this wrong but: the paragraph says that non-strongly-pure functions receive no special treatment, but then the next paragraph adds special treatment for a subset of non-strongly-pure functions... :)

It's all nitpicking of course, and now things may be obvious. But one year from now I'm sure we'll have trouble figuring out what was really meant... :S

December 23, 2016
On 12/23/2016 01:53 PM, Johan Engelen wrote:
> Perhaps I read this wrong but: the paragraph says that non-strongly-pure
> functions receive no special treatment, but then the next paragraph adds
> special treatment for a subset of non-strongly-pure functions... :)

Fixed. Keep destruction coming. -- Andrei