Jump to page: 1 2 3
Thread overview
Re: purity question
May 29, 2017
Jonathan M Davis
May 29, 2017
Brad Roberts
May 29, 2017
Stefan Koch
May 29, 2017
Brad Roberts
May 29, 2017
Stefan Koch
May 29, 2017
Jonathan M Davis
May 29, 2017
Brad Roberts
May 29, 2017
ketmar
May 29, 2017
ketmar
May 29, 2017
Seb
May 29, 2017
Jonathan M Davis
May 30, 2017
Rene Zwanenburg
May 30, 2017
ketmar
May 30, 2017
Rene Zwanenburg
May 30, 2017
ketmar
May 30, 2017
Biotronic
May 30, 2017
ag0aep6g
May 30, 2017
Jonathan M Davis
May 30, 2017
H. S. Teoh
May 29, 2017
Jonathan M Davis
May 29, 2017
Brad Roberts
May 29, 2017
Brad Roberts
May 29, 2017
Jonathan M Davis
May 28, 2017
On Sunday, May 28, 2017 16:49:16 Brad Roberts via Digitalmars-d-learn wrote:
> Is there a mechanism for declaring something pure when it's built from parts which individually aren't?
>
> string foo(string s)
> {
>      // do something arbitrarily complex with s that doesn't touch
> globals or change global state except possibly state of the heap or gc
>      return s;
> }

Either everything the function is doing is pure, or it can't be pure without cheating, and cheating is usually a bad idea. There is no @trusted equivalent for pure. Now, if you're absolutely sure that the function doesn't do anything that's actually going to run afoul of pure and what the compiler will assume about pure functions, but it's using stuff that wasn't properly marked as pure, or does something that can't be marked as pure for some reason but in this context would actually be fine, you can cast.

For instance, std.datetime's LocalTime is a singleton which gets the timezone conversion from the system. Its operations can't be pure, but it can be constructed as pure, and because it's a singleton, it will return the same instance every time. Originally, a static constructor was used to initialize the singleton (which therefore worked great with pure), but because of circular dependency pain with static constructors, we had to get rid of the static constructors in std.datetime. The solution was to move the initialization into another function which was not pure, and then when we call it from theh singleton function, we cast it to pure:

    static immutable(LocalTime) opCall() @trusted pure nothrow
    {
        alias FuncType = @safe pure nothrow immutable(LocalTime) function();
        return (cast(FuncType)&singleton)();
    }

    static immutable(LocalTime) singleton() @trusted
    {
        import core.stdc.time : tzset;
        import std.concurrency : initOnce;
        static instance = new immutable(LocalTime)();
        static shared bool guard;
        initOnce!guard({tzset(); return true;}());
        return instance;
    }

Now, in the vast majority of cases, I would strongly advise against cheating in this manner, because you need to be absolutely sure that you're not actually violating pure (particularly what the compiler will assume about pure) by what you're doing, and usually what it comes down to is that something that your function is calling needs to be fixed so that it can be inferred as pure, or it simply needs to be marked as pure. Casting is a horrible hack that should only be used as a last resort and only when you're 100% sure that it's fine. However, you _can_ use casting as a backdoor if you really have to.

- Jonathan M Davis

May 28, 2017
On 5/28/2017 5:34 PM, Jonathan M Davis via Digitalmars-d-learn wrote:
> On Sunday, May 28, 2017 16:49:16 Brad Roberts via Digitalmars-d-learn wrote:
>> Is there a mechanism for declaring something pure when it's built from
>> parts which individually aren't?
>>
>> string foo(string s)
>> {
>>       // do something arbitrarily complex with s that doesn't touch
>> globals or change global state except possibly state of the heap or gc
>>       return s;
>> }
> <snip lecture> you can cast </snip lecture>
>

Ok, so there essentially isn't.  I'm well aware of the risks of lying to the compiler, but it's also not sufficiently smart to unravel complex code.  Combined with there being interesting parts of the standard libraries that themselves aren't marked pure, there's a real need for escape hatches.  A simple example: anything that has a malloc/free pair.
May 29, 2017
On Monday, 29 May 2017 at 00:53:25 UTC, Brad Roberts wrote:
> On 5/28/2017 5:34 PM, Jonathan M Davis via Digitalmars-d-learn wrote:
>> On Sunday, May 28, 2017 16:49:16 Brad Roberts via Digitalmars-d-learn wrote:
>>> Is there a mechanism for declaring something pure when it's built from
>>> parts which individually aren't?
>>>
>>> string foo(string s)
>>> {
>>>       // do something arbitrarily complex with s that doesn't touch
>>> globals or change global state except possibly state of the heap or gc
>>>       return s;
>>> }
>> <snip lecture> you can cast </snip lecture>
>>
>
> Ok, so there essentially isn't.  I'm well aware of the risks of lying to the compiler, but it's also not sufficiently smart to unravel complex code.  Combined with there being interesting parts of the standard libraries that themselves aren't marked pure, there's a real need for escape hatches.  A simple example: anything that has a malloc/free pair.

There is

void[] myPureMalloc(uint size) pure @trusted nothrow @nogc
{
   alias pure_malloc_t = pure nothrow void* function(size_t size);
   return (cast(pure_malloc_t)malloc)(size)[0 .. size];
}
May 28, 2017
On 5/28/2017 6:01 PM, Stefan Koch via Digitalmars-d-learn wrote:
> On Monday, 29 May 2017 at 00:53:25 UTC, Brad Roberts wrote:
>> On 5/28/2017 5:34 PM, Jonathan M Davis via Digitalmars-d-learn wrote:
>>> On Sunday, May 28, 2017 16:49:16 Brad Roberts via Digitalmars-d-learn wrote:
>>>> Is there a mechanism for declaring something pure when it's built from
>>>> parts which individually aren't?
>>>>
>>>> string foo(string s)
>>>> {
>>>>       // do something arbitrarily complex with s that doesn't touch
>>>> globals or change global state except possibly state of the heap or gc
>>>>       return s;
>>>> }
>>> <snip lecture> you can cast </snip lecture>
>>>
>>
>> Ok, so there essentially isn't.  I'm well aware of the risks of lying to the compiler, but it's also not sufficiently smart to unravel complex code.  Combined with there being interesting parts of the standard libraries that themselves aren't marked pure, there's a real need for escape hatches.  A simple example: anything that has a malloc/free pair.
>
> There is
>
> void[] myPureMalloc(uint size) pure @trusted nothrow @nogc
> {
>    alias pure_malloc_t = pure nothrow void* function(size_t size);
>    return (cast(pure_malloc_t)malloc)(size)[0 .. size];
> }

That's still a cast.  It's a nice way to isolate the cast, but it's clearly still there.  And as I said, that's just one simple example.
May 29, 2017
On Monday, 29 May 2017 at 01:01:46 UTC, Stefan Koch wrote:
>
> There is
>
void[] myPureMalloc(uint size) pure @trusted nothrow @nogc
{
   import core.stdc.stdlib : malloc;
   alias pure_malloc_t = @nogc pure nothrow void* function(size_t size);
   return (cast(pure_malloc_t)&malloc)(size)[0 .. size];
}

This is the fixed version.
May 28, 2017
On Monday, May 29, 2017 01:01:46 Stefan Koch via Digitalmars-d-learn wrote:
> On Monday, 29 May 2017 at 00:53:25 UTC, Brad Roberts wrote:
> > On 5/28/2017 5:34 PM, Jonathan M Davis via Digitalmars-d-learn
> >
> > wrote:
> >> On Sunday, May 28, 2017 16:49:16 Brad Roberts via
> >>
> >> Digitalmars-d-learn wrote:
> >>> Is there a mechanism for declaring something pure when it's
> >>> built from
> >>> parts which individually aren't?
> >>>
> >>> string foo(string s)
> >>> {
> >>>
> >>>       // do something arbitrarily complex with s that doesn't
> >>>
> >>> touch
> >>> globals or change global state except possibly state of the
> >>> heap or gc
> >>>
> >>>       return s;
> >>>
> >>> }
> >>
> >> <snip lecture> you can cast </snip lecture>
> >
> > Ok, so there essentially isn't.  I'm well aware of the risks of lying to the compiler, but it's also not sufficiently smart to unravel complex code.  Combined with there being interesting parts of the standard libraries that themselves aren't marked pure, there's a real need for escape hatches.  A simple example: anything that has a malloc/free pair.
>
> There is
>
> void[] myPureMalloc(uint size) pure @trusted nothrow @nogc
> {
>     alias pure_malloc_t = pure nothrow void* function(size_t size);
>     return (cast(pure_malloc_t)malloc)(size)[0 .. size];
> }

There was a whole discussion or 3 is PRs about making malloc pure, and IIRC, it was done and then decided that it wasn't safe to do some for one reason or another (IIRC, it had to do with what would happen when calls were elided, because the caller was strongly pure, but I'm not sure). So, I'd be _very_ careful about deciding that it was safe to call malloc in pure code. I expect that it's just fine in some contexts, but it's easy enough to screw up and mark something as pure when it really shouldn't be because of some detail you missed that you should be _really_ careful about decided to cast to pure.

- Jonathan M Davis

May 28, 2017
On Sunday, May 28, 2017 17:53:25 Brad Roberts via Digitalmars-d-learn wrote:
> On 5/28/2017 5:34 PM, Jonathan M Davis via Digitalmars-d-learn wrote:
> > On Sunday, May 28, 2017 16:49:16 Brad Roberts via Digitalmars-d-learn
wrote:
> >> Is there a mechanism for declaring something pure when it's built from parts which individually aren't?
> >>
> >> string foo(string s)
> >> {
> >>
> >>       // do something arbitrarily complex with s that doesn't touch
> >>
> >> globals or change global state except possibly state of the heap or gc
> >>
> >>       return s;
> >>
> >> }
> >
> > <snip lecture> you can cast </snip lecture>
>
> Ok, so there essentially isn't.  I'm well aware of the risks of lying to the compiler, but it's also not sufficiently smart to unravel complex code.  Combined with there being interesting parts of the standard libraries that themselves aren't marked pure, there's a real need for escape hatches.

Well, the big thing is that there are a number of functions in Phobos that need to have someone go over them and fix them so that they are inferred to be pure when they should be. Some work has been done in that area, and the situation is certainly better than it once was, but not enough work has been done to make it so that we can at all reasonably that everything in Phobos that should be pure is and what isn't shouldn't be.

> A simple example: anything that has a malloc/free pair.

Yeah, if you do it right, you should be fine, but you have to do it right, and it's very easy to miss some detail that makes it wrong to insist to the compiler that what you're doing is pure. So, arguably, having anything more user-friendly than a cast is pretty dangerous. Having folks slap something like @assume_pure on things (if there were such an attribute) could be pretty risky. We already have enough trouble with @trusted on that front. Ultimately, we want to be in a position where needing to get around pure is very rare, and in most cases, I'd just tell folks to give up on pure on that piece of code rather than trying to hack it into working.

- Jonathan M Davis

May 28, 2017
On 5/28/2017 6:27 PM, Jonathan M Davis via Digitalmars-d-learn wrote:
> On Monday, May 29, 2017 01:01:46 Stefan Koch via Digitalmars-d-learn wrote:
>> On Monday, 29 May 2017 at 00:53:25 UTC, Brad Roberts wrote:
>>> On 5/28/2017 5:34 PM, Jonathan M Davis via Digitalmars-d-learn
>>>
>>> wrote:
>>>> On Sunday, May 28, 2017 16:49:16 Brad Roberts via
>>>>
>>>> Digitalmars-d-learn wrote:
>>>>> Is there a mechanism for declaring something pure when it's
>>>>> built from
>>>>> parts which individually aren't?
>>>>>
>>>>> string foo(string s)
>>>>> {
>>>>>
>>>>>        // do something arbitrarily complex with s that doesn't
>>>>>
>>>>> touch
>>>>> globals or change global state except possibly state of the
>>>>> heap or gc
>>>>>
>>>>>        return s;
>>>>>
>>>>> }
>>>> <snip lecture> you can cast </snip lecture>
>>> Ok, so there essentially isn't.  I'm well aware of the risks of
>>> lying to the compiler, but it's also not sufficiently smart to
>>> unravel complex code.  Combined with there being interesting
>>> parts of the standard libraries that themselves aren't marked
>>> pure, there's a real need for escape hatches.  A simple
>>> example: anything that has a malloc/free pair.
>> There is
>>
>> void[] myPureMalloc(uint size) pure @trusted nothrow @nogc
>> {
>>      alias pure_malloc_t = pure nothrow void* function(size_t size);
>>      return (cast(pure_malloc_t)malloc)(size)[0 .. size];
>> }
> There was a whole discussion or 3 is PRs about making malloc pure, and IIRC,
> it was done and then decided that it wasn't safe to do some for one reason
> or another (IIRC, it had to do with what would happen when calls were
> elided, because the caller was strongly pure, but I'm not sure). So, I'd be
> _very_ careful about deciding that it was safe to call malloc in pure code.
> I expect that it's just fine in some contexts, but it's easy enough to screw
> up and mark something as pure when it really shouldn't be because of some
> detail you missed that you should be _really_ careful about decided to cast
> to pure.
>
> - Jonathan M Davis

That's one reason I explicitly referenced malloc/free pairs.  It's a lot easier to be sure that those together aren't violating purity.
May 28, 2017
On Sunday, May 28, 2017 18:39:02 Brad Roberts via Digitalmars-d-learn wrote:
> On 5/28/2017 6:27 PM, Jonathan M Davis via Digitalmars-d-learn wrote:
> > There was a whole discussion or 3 is PRs about making malloc pure, and IIRC, it was done and then decided that it wasn't safe to do some for one reason or another (IIRC, it had to do with what would happen when calls were elided, because the caller was strongly pure, but I'm not sure). So, I'd be _very_ careful about deciding that it was safe to call malloc in pure code. I expect that it's just fine in some contexts, but it's easy enough to screw up and mark something as pure when it really shouldn't be because of some detail you missed that you should be _really_ careful about decided to cast to pure.
>
> That's one reason I explicitly referenced malloc/free pairs.  It's a lot easier to be sure that those together aren't violating purity.

Agreed. But it's the intricacies like that which make having a clean backdoor for pure a bit dangerous, much as it would be nice when you actually need to do it and know what you're doing well enough to get it right.

- Jonathan M Davis

May 28, 2017
On 5/28/2017 6:36 PM, Jonathan M Davis via Digitalmars-d-learn wrote:
> On Sunday, May 28, 2017 17:53:25 Brad Roberts via Digitalmars-d-learn wrote:
>> On 5/28/2017 5:34 PM, Jonathan M Davis via Digitalmars-d-learn wrote:
>>> On Sunday, May 28, 2017 16:49:16 Brad Roberts via Digitalmars-d-learn
> wrote:
>>>> Is there a mechanism for declaring something pure when it's built from
>>>> parts which individually aren't?
>>>>
>>>> string foo(string s)
>>>> {
>>>>
>>>>        // do something arbitrarily complex with s that doesn't touch
>>>>
>>>> globals or change global state except possibly state of the heap or gc
>>>>
>>>>        return s;
>>>>
>>>> }
>>> <snip lecture> you can cast </snip lecture>
>> Ok, so there essentially isn't.  I'm well aware of the risks of lying to
>> the compiler, but it's also not sufficiently smart to unravel complex
>> code.  Combined with there being interesting parts of the standard
>> libraries that themselves aren't marked pure, there's a real need for
>> escape hatches.
> Well, the big thing is that there are a number of functions in Phobos that
> need to have someone go over them and fix them so that they are inferred to
> be pure when they should be. Some work has been done in that area, and the
> situation is certainly better than it once was, but not enough work has been
> done to make it so that we can at all reasonably that everything in Phobos
> that should be pure is and what isn't shouldn't be.
>
>> A simple example: anything that has a malloc/free pair.
> Yeah, if you do it right, you should be fine, but you have to do it right,
> and it's very easy to miss some detail that makes it wrong to insist to the
> compiler that what you're doing is pure. So, arguably, having anything more
> user-friendly than a cast is pretty dangerous. Having folks slap something
> like @assume_pure on things (if there were such an attribute) could be
> pretty risky. We already have enough trouble with @trusted on that front.
> Ultimately, we want to be in a position where needing to get around pure is
> very rare, and in most cases, I'd just tell folks to give up on pure on that
> piece of code rather than trying to hack it into working.
>
> - Jonathan M Davis

Again, of course it's possible to do it wrong.  Escape hatches are like that.  And of course things are being worked on and improved, I'm one of the ones that's done a good bit of that at various points in time.  I'm really not seeking a lesson or lecture in why it's dangerous.

Here's the bug that I'm digging into today, a clear example of an api that _should_ be pure, but based on the implementation is rather difficult for the compiler to infer.
« First   ‹ Prev
1 2 3