May 15, 2014
On 15 May 2014 22:30, Steven Schveighoffer via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On Thu, 15 May 2014 07:52:20 -0400, Manu via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
>> On 15 May 2014 10:50, Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>>>
>>> On 5/14/2014 5:03 PM, Meta wrote:
>>>>
>>>>
>>>> Allocating memory through new and malloc should always be pure, I think,
>>>> because
>>>> failure either returns null in malloc's case,
>>>
>>>
>>>
>>> malloc cannot be pure if, with the same arguments, it returns null
>>> sometimes
>>> and not other times.
>>
>>
>> Even if it doesn't fail, malloc called with identical arguments still
>> returns a different result every time.
>> You can't factor malloc outside a loop and cache the result because
>> it's being called repeatedly with the same argument like you're
>> supposed to be able to do with any other pure function.
>> You shouldn't be able to do that with gcalloc either... how can gcalloc be
>> pure?
>
>
> That only applies to strong-pure functions. malloc would be weak-pure, since it returns a mutable pointer.

Why should returning a mutable pointer imply weak purity?
May 15, 2014
On Thu, 15 May 2014 09:24:54 -0400, Ola Fosheim Grøstad <ola.fosheim.grostad+dlang@gmail.com> wrote:

> On Thursday, 15 May 2014 at 12:57:43 UTC, Steven Schveighoffer wrote:
>> To be honest, code that would exploit such an anomaly is only ever used in "proof" exercises, and never in real code. I don't think it's an issue.
>
> That's the wrong attitude to take when it comes to the compiler and runtime.

There are always ways around the guarantees. This one isn't as detectable, since there is no "red-flag" cast or attribute. But I see no utility in such code.

> If it verifies, it should verify. Not mostly, but fully, otherwise "weakly pure" becomes a programmer guarantee. Which is ok too, but either the one or the other.

Memory allocation is an exception to purity that is widely considered OK, because of the practical need for allocating memory from a heap to do work. In general, it's the block of data that is important, not it's address. Where it comes from is of no consequence.

> How can you prove that such issues don't arise in real code?

Of course I can't prove it doesn't arise, but I can't think of any use cases to do something significant based on the address of heap data in relation to another unrelated heap block.

Perhaps you can show a use case for it?

The other thing to think about is that such code won't serve the purpose for which it was written! randomBit won't be random because it will likely be memoized.

In any case, the alternative is to have D pure functions avoid using pointers. It's as drastic as that.

-Steve
May 15, 2014
On Thu, 15 May 2014 09:40:03 -0400, Manu via Digitalmars-d <digitalmars-d@puremagic.com> wrote:

> On 15 May 2014 22:30, Steven Schveighoffer via Digitalmars-d
> <digitalmars-d@puremagic.com> wrote:
>> On Thu, 15 May 2014 07:52:20 -0400, Manu via Digitalmars-d
>> <digitalmars-d@puremagic.com> wrote:
>>
>>> On 15 May 2014 10:50, Walter Bright via Digitalmars-d
>>> <digitalmars-d@puremagic.com> wrote:
>>>>
>>>> On 5/14/2014 5:03 PM, Meta wrote:
>>>>>
>>>>>
>>>>> Allocating memory through new and malloc should always be pure, I think,
>>>>> because
>>>>> failure either returns null in malloc's case,
>>>>
>>>>
>>>>
>>>> malloc cannot be pure if, with the same arguments, it returns null
>>>> sometimes
>>>> and not other times.
>>>
>>>
>>> Even if it doesn't fail, malloc called with identical arguments still
>>> returns a different result every time.
>>> You can't factor malloc outside a loop and cache the result because
>>> it's being called repeatedly with the same argument like you're
>>> supposed to be able to do with any other pure function.
>>> You shouldn't be able to do that with gcalloc either... how can gcalloc be
>>> pure?
>>
>>
>> That only applies to strong-pure functions. malloc would be weak-pure, since
>> it returns a mutable pointer.
>
> Why should returning a mutable pointer imply weak purity?

Because if it were strong-pure, the compiler could optimize two sequential calls as returning the same exact thing. Imagine if a constructor to a mutable object always returned the same object because the constructor's parameters were immutable. It would be useless.

-Steve
May 15, 2014
On Thu, 15 May 2014 09:28:57 -0400, Dicebot <public@dicebot.lv> wrote:

> On Thursday, 15 May 2014 at 12:57:43 UTC, Steven Schveighoffer wrote:
>> On Thu, 15 May 2014 02:50:05 -0400, Ola Fosheim Grøstad <ola.fosheim.grostad+dlang@gmail.com> wrote:
>>
>>> On Thursday, 15 May 2014 at 06:29:06 UTC, bearophile wrote:
>>>> A little example of D purity (this compiles):
>>>
>>>> bool randomBit() pure nothrow @safe {
>>>>    return (new int[1].ptr) > (new int[1].ptr);
>>>> }
>>>
>>> Yes, and then you may as well allow a random generator with private globals. Because memoing is no longer sound anyway.
>>
>> This has nothing to do with allocators being pure. They must be pure as a matter of practicality.
>>
>> The issue that allows the above anomaly is using the address of something. There is a reason functional languages do not allow these types of things. But in functional languages, allocating is allowed.
>
> Which is what makes D pure lint-like helper and not effective optimization enabler. This part of type system was under-designed initially, it should have been much more restrictive.

It's easy to say this, but the restrictions to apply would be draconian. I think it would be nearly impossible to prevent such abuses in a language with explicit references.

>> To be honest, code that would exploit such an anomaly is only ever used in "proof" exercises, and never in real code. I don't think it's an issue.
>
> This is not true. Because of such code you can't ever automatically memoize strongly pure function results by compiler. A very practical concern.

I think you can still memoize these cases. There is no reason for the language to support a pure RNG.

-Steve
May 15, 2014
On Thursday, 15 May 2014 at 06:50:06 UTC, Ola Fosheim Grøstad wrote:
> On Thursday, 15 May 2014 at 06:29:06 UTC, bearophile wrote:
>> A little example of D purity (this compiles):
>
>> bool randomBit() pure nothrow @safe {
>>    return (new int[1].ptr) > (new int[1].ptr);
>> }
>
> Yes, and then you may as well allow a random generator with private globals. Because memoing is no longer sound anyway.

No, this particular example appears to be invalid code. Under the C memory model [1], the result of comparing two pointers to distinct objects (allocations) is undefined/unspecified behavior (I think it is undefined in C and unspecified in C++, but I don't remember the details).

Of course, you could rescue the example by casting the pointers to size_t or something along the lines, but this is something that could be disallowed in pure code.

David


[1] Which typically applies to D, unless defined otherwise. Our language specification is woefully incomplete in this regard, though.
May 15, 2014
On Thursday, 15 May 2014 at 13:40:16 UTC, Manu via Digitalmars-d wrote:
> Why should returning a mutable pointer imply weak purity?

The argument here is that a valid mutable pointer returned from a pure function could always point to a new allocation, as new is allowed in pure code.

More precisely, the only way to return a valid mutable pointer from a parameterless function is to make it point to a new allocation, so allowing memory allocations does not even preclude the inference of stronger guarantees here.

David
May 15, 2014
On Thursday, 15 May 2014 at 13:42:58 UTC, Steven Schveighoffer wrote:
> On Thu, 15 May 2014 09:24:54 -0400, Ola Fosheim Grøstad <ola.fosheim.grostad+dlang@gmail.com> wrote:
>> That's the wrong attitude to take when it comes to the compiler and runtime.
>
> There are always ways around the guarantees. This one isn't as detectable, since there is no "red-flag" cast or attribute. But I see no utility in such code.

I have to agree with Ola here. If you write a piece of pure, @safe code, there should be no way for compiler optimizations to make your code behave differently.

This is not only because implicitly allowing unsafe code would be against the design philosophy behind D, but also as attribute inference might tag functions as pure without the programmer explicitly specifying so.

> In any case, the alternative is to have D pure functions avoid using pointers. It's as drastic as that.

I'd suspect that it is enough to disallow using the content of pointers explicitly, i.e. as a sequence of bytes instead of just a handle to an object.

David
May 15, 2014
On Thu, 15 May 2014 10:42:00 -0400, David Nadlinger <code@klickverbot.at> wrote:

> On Thursday, 15 May 2014 at 13:42:58 UTC, Steven Schveighoffer wrote:
>> On Thu, 15 May 2014 09:24:54 -0400, Ola Fosheim Grøstad <ola.fosheim.grostad+dlang@gmail.com> wrote:
>>> That's the wrong attitude to take when it comes to the compiler and runtime.
>>
>> There are always ways around the guarantees. This one isn't as detectable, since there is no "red-flag" cast or attribute. But I see no utility in such code.
>
> I have to agree with Ola here. If you write a piece of pure, @safe code, there should be no way for compiler optimizations to make your code behave differently.

In general, I would say any code that performs differently after optimization is not preferable. But in this case, you have ignored the rules, and the result is not exactly incorrect or unsafe. In fact, you can't really argue that it's invalid (randomBit could legitimately always return true or false, even in a non-optimized program).

The question here is, can we come up with a static rule that is effective, but not cripplingly prohibitive?

> This is not only because implicitly allowing unsafe code would be against the design philosophy behind D, but also as attribute inference might tag functions as pure without the programmer explicitly specifying so.

So far, I haven't seen code that's unsafe only if optimized. Have an example?

>> In any case, the alternative is to have D pure functions avoid using pointers. It's as drastic as that.
>
> I'd suspect that it is enough to disallow using the content of pointers explicitly, i.e. as a sequence of bytes instead of just a handle to an object.

This means format("%x", ptr) isn't allowed to be pure?

What about calculating index offsets? Note that pointer math between two pointers on the same block of memory is perfectly legitimate.

I would expect that someone could be able to write a type equivalent to a slice, and it should be allowed to be pure.

-Steve
May 15, 2014
David Nadlinger:

> I'd suspect that it is enough to disallow using the content of pointers explicitly, i.e. as a sequence of bytes instead of just a handle to an object.

Yes, if you allow only a referentially pure view of pointers, then you have a strong purity.

Bye,
bearophile
May 15, 2014
On 5/15/14, 3:31 AM, luka8088 wrote:
> Yeah, I read all about weak/string purity and I do understand the
> background. I was talking about strong purity, maybe I should pointed
> that out.
>
> So, to correct myself: As I understood strong purity implies
> memoization. Am I correct?

Yes, as long as you don't rely on distinguishing objects by address.

Purity of allocation is frequently assumed by functional languages because without it it would be difficult to get much work done. Then, most functional languages make it difficult or impossible to distinguish values by their address. In D that's easy. A D programmer needs to be aware of that, and I think that's fine.


Andrei