April 24, 2015
On Friday, 24 April 2015 at 20:55:02 UTC, Steven Schveighoffer wrote:
> But I can check memory usage size and see global state has been altered.
>

OK, if you want to play that game, don't access memory ever, that is global state. I mean, even if the memory is read only you may end up affecting the MMU, so that is definitively a no go. Also, you'd better have only a one instruction loop in your program as otherwise you'll affect the program counter, a definitively visible state from the user.

>> When you free, you potentially alter references anywhere outside your
>> "pure" function. It must not be pure.
>
> When you do ANYTHING to mutable data, you potentially alter references outside your pure function. This is not a disqualifier. It's accessing global sate directly that wasn't passed to you that is a disqualifier.
>
> -Steve

pure function can access global immutable state that wasn't passed to it, so you may want to revise your definition.
April 24, 2015
On 4/24/15 5:07 PM, deadalnix wrote:
> On Friday, 24 April 2015 at 20:55:02 UTC, Steven Schveighoffer wrote:
>> But I can check memory usage size and see global state has been altered.
>>
>
> OK, if you want to play that game, don't access memory ever, that is
> global state. I mean, even if the memory is read only you may end up
> affecting the MMU, so that is definitively a no go. Also, you'd better
> have only a one instruction loop in your program as otherwise you'll
> affect the program counter, a definitively visible state from the user.

All I'm saying is that GC.malloc alters global state. I agree that it's OK to pretend that it doesn't because as long as you agree not to base things on this knowledge, you are fine calling pure functions that use GC.

It's possible to do things like this, to make pure functions "unpure":

bool foo() pure
{
   return new int(0) < new int(0);
}

We just agree to ignore that aspect. And I'm OK with it. As I'm OK with ignoring the bad things you can do with C malloc or C free that make things impure.

>>> When you free, you potentially alter references anywhere outside your
>>> "pure" function. It must not be pure.
>>
>> When you do ANYTHING to mutable data, you potentially alter references
>> outside your pure function. This is not a disqualifier. It's accessing
>> global sate directly that wasn't passed to you that is a disqualifier.
>
> pure function can access global immutable state that wasn't passed to
> it, so you may want to revise your definition.

Sure: s/accessing/altering, my mistake.

-Steve
April 25, 2015
On Friday, 24 April 2015 at 23:27:36 UTC, Steven Schveighoffer wrote:
> All I'm saying is that GC.malloc alters global state. I agree that it's OK to pretend that it doesn't because as long as you agree not to base things on this knowledge, you are fine calling pure functions that use GC.

A lie that is universally agreed upon is virtually indistinguishable from the truth.

the reasons against making malloc/free pure are very... thin.

Walter himself attempted to make them pure only last week(!)
https://github.com/D-Programming-Language/druntime/pull/1221
April 25, 2015
On 4/24/15 8:30 PM, weaselcat wrote:
> On Friday, 24 April 2015 at 23:27:36 UTC, Steven Schveighoffer wrote:
>> All I'm saying is that GC.malloc alters global state. I agree that
>> it's OK to pretend that it doesn't because as long as you agree not to
>> base things on this knowledge, you are fine calling pure functions
>> that use GC.
>
> A lie that is universally agreed upon is virtually indistinguishable
> from the truth.
>
> the reasons against making malloc/free pure are very... thin.
>
> Walter himself attempted to make them pure only last week(!)
> https://github.com/D-Programming-Language/druntime/pull/1221

I hadn't noticed that.

And linked from there is the reason I was shown that free cannot be pure (from a while ago):

void freeIt(immutable(int)* x) pure { free(cast(int *)x); }

A compiler could legally just not even call this function.

It's a pretty compelling reason.

I have a feeling that we can't make free generally pure. When needed, it will have to be hacked somehow in a controlled way.

Or we could make some rule that you should never cast immutable pointers to mutable in order to free them. Doesn't sound very enforceable...

-Steve
April 26, 2015
On Friday, 24 April 2015 at 23:27:36 UTC, Steven Schveighoffer wrote:
>> pure function can access global immutable state that wasn't passed to
>> it, so you may want to revise your definition.
>
> Sure: s/accessing/altering, my mistake.
>
> -Steve

That is the whole point. See it as follow: GC.malloc create new state. As long as this new state doesn't escape the pure function, it is as if that state was local.
April 27, 2015
On 4/25/15 11:06 PM, deadalnix wrote:
> On Friday, 24 April 2015 at 23:27:36 UTC, Steven Schveighoffer wrote:
>>> pure function can access global immutable state that wasn't passed to
>>> it, so you may want to revise your definition.
>>
>> Sure: s/accessing/altering, my mistake.
>>
>
> That is the whole point. See it as follow: GC.malloc create new state.
> As long as this new state doesn't escape the pure function, it is as if
> that state was local.

I get that, you can apply the same thing for free. No reason this can't be a pure function:

void foo()
{
   int *x = cast(int *)malloc(sizeof(int));
   *x = 0;
   scope(exit) free(x);
}

But there are other problems, as I pointed out in another post.

-Steve
April 27, 2015
On Monday, 27 April 2015 at 10:50:12 UTC, Steven Schveighoffer wrote:
> On 4/25/15 11:06 PM, deadalnix wrote:
>> On Friday, 24 April 2015 at 23:27:36 UTC, Steven Schveighoffer wrote:
>>>> pure function can access global immutable state that wasn't passed to
>>>> it, so you may want to revise your definition.
>>>
>>> Sure: s/accessing/altering, my mistake.
>>>
>>
>> That is the whole point. See it as follow: GC.malloc create new state.
>> As long as this new state doesn't escape the pure function, it is as if
>> that state was local.
>
> I get that, you can apply the same thing for free. No reason this can't be a pure function:
>

With malloc, you have 2 new thing is userland:
 - A new state, which, as for the GC case could be considered pure.
 - A global state that you have to maintain (the fact that you allocated and must free) and this should not be pure.

> void foo()
> {
>    int *x = cast(int *)malloc(sizeof(int));
>    *x = 0;
>    scope(exit) free(x);
> }
>
> But there are other problems, as I pointed out in another post.
>
> -Steve

In that specific use case, I think this is fair to consider that code pure, or at least pretend it is. D being a system programming language, you can pretend this is pure.
March 31, 2016
On Friday, 24 April 2015 at 15:05:15 UTC, anonymous wrote:
> GC.malloc is marked pure. But it isn't, is it?
>
> This should hold for a pure function:
>     assert(f(x) == f(x));
> This fails with GC.malloc, of course.
>
> Or consider this:
>     auto v = f(x);
>     auto w = f(x);
> When f is pure, a compiler should be free to reuse the value of v for w. That's no good with GC.malloc, obviously.

A solution is to fake purity through

extern(C) pure nothrow @system @nogc
{
    void* malloc(size_t size);
    void* realloc(void* ptr, size_t size);
    void free(void* ptr);
}

Pay attention to the use of malloc() though.

Note that this makes malloc() strongly pure (its single parameters is passed by value). Therefore successive calls to malloc() with the same argument will be optimized away and all those calls will return the same values as the first call. When used in allocators this shouldn't be a problem, though.

Used successfully at

https://github.com/nordlow/justd/blob/master/packedarray.d#L6
April 01, 2016
On 31.03.2016 23:25, Nordlöw wrote:
> A solution is to fake purity through
>
> extern(C) pure nothrow @system @nogc
> {
>      void* malloc(size_t size);
>      void* realloc(void* ptr, size_t size);
>      void free(void* ptr);
> }
>
> Pay attention to the use of malloc() though.
>
> Note that this makes malloc() strongly pure (its single parameters is
> passed by value).

As has been explained to me in this very thread, the fact that malloc returns a pointer to mutable data makes it not strongly pure.

See http://klickverbot.at/blog/2012/05/purity-in-d/#indirections-in-the-return-type

> Therefore successive calls to malloc() with the same
> argument will be optimized away and all those calls will return the same
> values as the first call. When used in allocators this shouldn't be a
> problem, though.

Uh, as far as I see, that would be catastrophic.

I feel like I must be missing something here again, but it looks like dmd actually gets this wrong:

----
extern(C) void* malloc(size_t size) pure nothrow;

void main()
{
    auto a = cast(ubyte*) malloc(1);
    auto b = cast(ubyte*) malloc(1);

    import std.stdio;
    writeln(a is b); /* should be false */
    *a = 1;
    *b = 2;
    writeln(*a); /* should be 1 */
}
----

Compiled with dmd and no optimization switches, this prints "false" and "1" as expected.
Compiled with `dmd -O -release`, this prints "true" and "2". The heck?
With `ldc2 -O5 -release`: "false" and "1". No problem there.

This looks like a serious bug in dmd to me.
March 31, 2016
On Thursday, 31 March 2016 at 22:18:03 UTC, ag0aep6g wrote:
> On 31.03.2016 23:25, Nordlöw wrote:
>> A solution is to fake purity through
>>
>> extern(C) pure nothrow @system @nogc
>> {
>>      void* malloc(size_t size);
>>      void* realloc(void* ptr, size_t size);
>>      void free(void* ptr);
>> }
>>
>> Pay attention to the use of malloc() though.
>>
>> Note that this makes malloc() strongly pure (its single parameters is
>> passed by value).
>
> As has been explained to me in this very thread, the fact that malloc returns a pointer to mutable data makes it not strongly pure.
>

Is it not pure, strong or weak. GC.malloc is pure because the memory is going to be garbage collected. malloc is not pure as the memory need to be freed. The state in the allocator is exposed to the program.

> Uh, as far as I see, that would be catastrophic.
>
> I feel like I must be missing something here again, but it looks like dmd actually gets this wrong:
>
> ----
> extern(C) void* malloc(size_t size) pure nothrow;
>
> void main()
> {
>     auto a = cast(ubyte*) malloc(1);
>     auto b = cast(ubyte*) malloc(1);
>
>     import std.stdio;
>     writeln(a is b); /* should be false */
>     *a = 1;
>     *b = 2;
>     writeln(*a); /* should be 1 */
> }
> ----
>
> Compiled with dmd and no optimization switches, this prints "false" and "1" as expected.
> Compiled with `dmd -O -release`, this prints "true" and "2". The heck?
> With `ldc2 -O5 -release`: "false" and "1". No problem there.
>
> This looks like a serious bug in dmd to me.

Lie to the compiler and it will punish you for it.

DMD optimizes malloc because the only reason it would have to return a different pointer would be internal state (and in fact, it indeed has internal state).