March 21, 2014
On 3/20/14, 6:52 PM, Steven Schveighoffer wrote:
> On Thu, 20 Mar 2014 21:44:38 -0400, bearophile
> <bearophileHUGS@lycos.com> wrote:
>
>> Steven Schveighoffer:
>>
>>> This is also a pure function:
>>>
>>> pure int foo()
>>> {
>>>    while(1) {}
>>>    return 0;
>>> }
>>>
>>> Pure doesn't mean "bug free".
>>
>> Thankfully the D compiler catches the bug :-)
>>
>> test.d(3,4): Warning: statement is not reachable
>
> I can make it not catch that error, but that is not the bug. The bug is
> that it never returns, effectively deadlocking. The sample is
> intentionally short to demonstrate my point, I (obviously) didn't try to
> compile it.
>
> -Steve

My dream: pure @safe functions never cause deadlock.

Andrei

March 21, 2014
On Thu, 20 Mar 2014 22:14:59 -0400, Steven Schveighoffer <schveiguy@yahoo.com> wrote:

> On Thu, 20 Mar 2014 22:00:15 -0400, Walter Bright <newshound2@digitalmars.com> wrote:
>
>> On 3/20/2014 6:40 PM, Steven Schveighoffer wrote:
>>> How do they affect global state?
>>
>> Mutexes implicitly share state. It's the reason they exist. They can't be pure, because pure functions don't share state.
>
> I view it differently. I feel like locking and unlocking a mutex is pure. After calling lock, the same thing *always* happens. After unlocking, the same thing *always* happens. It's a weird thing -- when you lock a mutex, you own it after it's locked. It's no longer shared. It can be thought of as pulling memory out of the heap to temporarily own, and then putting it back, just like memory allocation (which is considered pure).

Thinking about it some more, I see what you mean -- an unshared mutex is useless.

But at the same time, some "logically" pure functions cannot be so without mutexes. E.g. memory allocation.

-Steve
March 21, 2014
On Friday, 21 March 2014 at 03:04:36 UTC, Steven Schveighoffer wrote:
> Thinking about it some more, I see what you mean -- an unshared mutex is useless.
>
> But at the same time, some "logically" pure functions cannot be so without mutexes. E.g. memory allocation.

Since when does "shared" => "impure" ?

If the function takes a pointer to shared data, then you are explicitly saying "this function depends on this shared data". But as long is it isn't referencing some *other* global directly, it is perfectly pure.

For example:

//----
int i = 5;

void foo(int* p) pure
{++p;}

void main() pure
{
    foo(&p);
}
//----

This is "textbook" of a pure function changing global state.

----------------

So the way I see it: shared => pinter to mutex => touching it is fair game => can be legit pure (IMO).
March 21, 2014
On 3/20/2014 7:02 PM, deadalnix wrote:
> On Friday, 21 March 2014 at 02:00:11 UTC, Walter Bright wrote:
>> Mutexes implicitly share state. It's the reason they exist. They can't be
>> pure, because pure functions don't share state.
>
> If you got that road, you can't allow memory allocators to be
> used in pure code. That isn't a good argument.

The analogy doesn't apply. Operator new is regarded as supplying memory from an infinite reservoir. There is no implicit global state.

The whole point of a mutex is to change state and share it. It's utterly different.
March 21, 2014
On 3/21/2014 12:03 AM, monarch_dodra wrote:
> On Friday, 21 March 2014 at 03:04:36 UTC, Steven Schveighoffer wrote:
>> Thinking about it some more, I see what you mean -- an unshared mutex is useless.
>>
>> But at the same time, some "logically" pure functions cannot be so without
>> mutexes. E.g. memory allocation.
>
> Since when does "shared" => "impure" ?
>
> If the function takes a pointer to shared data, then you are explicitly saying
> "this function depends on this shared data". But as long is it isn't referencing
> some *other* global directly, it is perfectly pure.

We've been using 'shared' here to mean shared with another piece of code that looks at the state, not 'shared' as in data shared amongst multiple threads.

March 21, 2014
On Friday, 21 March 2014 at 07:14:47 UTC, Walter Bright wrote:
> On 3/21/2014 12:03 AM, monarch_dodra wrote:
>> On Friday, 21 March 2014 at 03:04:36 UTC, Steven Schveighoffer wrote:
>>> Thinking about it some more, I see what you mean -- an unshared mutex is useless.
>>>
>>> But at the same time, some "logically" pure functions cannot be so without
>>> mutexes. E.g. memory allocation.
>>
>> Since when does "shared" => "impure" ?
>>
>> If the function takes a pointer to shared data, then you are explicitly saying
>> "this function depends on this shared data". But as long is it isn't referencing
>> some *other* global directly, it is perfectly pure.
>
> We've been using 'shared' here to mean shared with another piece of code that looks at the state, not 'shared' as in data shared amongst multiple threads.

Ok. That's a fair point. So in that case, our function is pointing at "data", and is allowed to mutate it, and observe its state.

Now, if *another* piece of code is doing the same thing at the same time (potentially mutating "data", does that still violate purity?

As long a the function doesn't access/mutate something via direct global state, it's pure, isn't it?

I think... it's tough to wrap your head around the issue.
March 21, 2014
On Friday, 21 March 2014 at 02:00:11 UTC, Walter Bright wrote:
> On 3/20/2014 6:40 PM, Steven Schveighoffer wrote:
>> How do they affect global state?
>
> Mutexes implicitly share state. It's the reason they exist. They can't be pure, because pure functions don't share state.

Locking a monitor is also a mutating operation and yet I believe you can have const synchronized methods.  They live somewhat outside the normal type system.  I don't see any point in having pure class methods, but what about:

pure int add(T)(const(T) a, const(T) b) {
    return a + b;
}

Where the variables above are instances of a synchronized class?  The operation would implicitly lock their monitors to perform the addition.
March 21, 2014
On 3/21/2014 12:59 AM, monarch_dodra wrote:
> Ok. That's a fair point. So in that case, our function is pointing at "data",
> and is allowed to mutate it, and observe its state.
>
> Now, if *another* piece of code is doing the same thing at the same time
> (potentially mutating "data", does that still violate purity?

A mutex essentially reads and writes a global flag, which other functions can also read and write.

That makes it NOT pure.

March 21, 2014
On 3/21/2014 9:28 AM, Sean Kelly wrote:
> On Friday, 21 March 2014 at 02:00:11 UTC, Walter Bright wrote:
>> On 3/20/2014 6:40 PM, Steven Schveighoffer wrote:
>>> How do they affect global state?
>>
>> Mutexes implicitly share state. It's the reason they exist. They can't be
>> pure, because pure functions don't share state.
>
> Locking a monitor is also a mutating operation and yet I believe you can have
> const synchronized methods.  They live somewhat outside the normal type system.
> I don't see any point in having pure class methods, but what about:
>
> pure int add(T)(const(T) a, const(T) b) {
>      return a + b;
> }
>
> Where the variables above are instances of a synchronized class? The operation
> would implicitly lock their monitors to perform the addition.

Yes, I think the pairing of lock/unlock of a mutex can be pure, but just a lock or just an unlock cannot be.
March 21, 2014
On Fri, 21 Mar 2014 14:18:05 -0400, Walter Bright <newshound2@digitalmars.com> wrote:

> On 3/21/2014 12:59 AM, monarch_dodra wrote:
>> Ok. That's a fair point. So in that case, our function is pointing at "data",
>> and is allowed to mutate it, and observe its state.
>>
>> Now, if *another* piece of code is doing the same thing at the same time
>> (potentially mutating "data", does that still violate purity?
>
> A mutex essentially reads and writes a global flag, which other functions can also read and write.
>
> That makes it NOT pure.

No, that's not the case. A mutex does not write a global flag, it writes a shared flag. And the flag is passed into it.

I still think straight locking and unlocking of mutexes should be pure, because of the exclusive nature of the data protected by them.

-Steve