February 16, 2012
Stewart Gordon:

> But if the method is pure, the compiler can automatically implement this as an optimisation.

Functions like toHash take nothing and return a single size_t (hash_t). Often you want to compute the hash value lazily, but this is not possible if toHash needs to be pure. A explicit optional @memoize annotation (similar to the std.functional.memoize) allows toHash to be both catching and safe. (I was also thinking about the idea of a @trusted_pure, but I don't like it, and I think it causes chaos).

Bye,
bearophile
February 16, 2012
Le 16/02/2012 12:12, Daniel Murphy a écrit :
> I mean, are we going to have a shared overload in Object for each of those
> functions?
>

I'd argue that in some cases, the sompiler should be able to generate a shared const function automatically from the const function. This has some limitation. I'm currently writting an article about that possibility and what are the limitations.

BTW, shared is - sadly - almost a stub right now, so it doesn't really matter.

> "Walter Bright"<newshound2@digitalmars.com>  wrote in message
> news:jhikus$227j$1@digitalmars.com...
>> On 2/16/2012 1:30 AM, Daniel Murphy wrote:
>>> What about shared?
>>
>> No! Shared is a special animal, and the user will still have to take care
>> to deal with synchronization issues.
>
>

February 16, 2012
On 02/16/2012 09:35 AM, Walter Bright wrote:
> These all need to be:
>
> const pure nothrow @safe
>
> Unless this is done, the utility of const, pure, nothrow and @safe is
> rather crippled.
>
> Any reason why they shouldn't be?

The utility of const, pure, nothrow and @safe is already rather crippled when using Phobos. It is a rather small minority of my pure functions that are effectively annotated pure, because most of Phobos is not properly annotated and the inference for templates does not work satisfactorily yet.

So imo, making opEquals, opCmp, toHash const pure nothrow @safe is blocked by properly annotating Phobos as well as the following issues:

Bugs in existing attribute inference:
http://d.puremagic.com/issues/show_bug.cgi?id=7205
http://d.puremagic.com/issues/show_bug.cgi?id=7511

Enhancement required for making templates const correct:
http://d.puremagic.com/issues/show_bug.cgi?id=7521


>
> One reason is memoization, aka lazy initialization, aka logical const. I
> don't believe these are worth it. If you must do it inside those
> functions (and functions that override them), you're on your own to make
> it work right (use unsafe casts, etc.).

It would be helpful if we could add cast(pure) for that purpose. The documentation could state that cast(pure) is only valid if the behaviour of the enclosing function still appears to be pure.
February 16, 2012
Le 16/02/2012 16:52, Timon Gehr a écrit :
> On 02/16/2012 09:35 AM, Walter Bright wrote:
>> These all need to be:
>>
>> const pure nothrow @safe
>>
>> Unless this is done, the utility of const, pure, nothrow and @safe is
>> rather crippled.
>>
>> Any reason why they shouldn't be?
>
> The utility of const, pure, nothrow and @safe is already rather crippled
> when using Phobos. It is a rather small minority of my pure functions
> that are effectively annotated pure, because most of Phobos is not
> properly annotated and the inference for templates does not work
> satisfactorily yet.
>
> So imo, making opEquals, opCmp, toHash const pure nothrow @safe is
> blocked by properly annotating Phobos as well as the following issues:
>
> Bugs in existing attribute inference:
> http://d.puremagic.com/issues/show_bug.cgi?id=7205
> http://d.puremagic.com/issues/show_bug.cgi?id=7511
>
> Enhancement required for making templates const correct:
> http://d.puremagic.com/issues/show_bug.cgi?id=7521
>
>

Good point. However, I don't think that should stop us.

BTW, what should happen if we write a opComp that isn't nothrow, @safe or pure ?
February 16, 2012
On 16/02/2012 13:05, bearophile wrote:
> Stewart Gordon:
>
>> But if the method is pure, the compiler can automatically implement this as an optimisation.
>
> Functions like toHash take nothing and return a single size_t (hash_t). Often you want
> to compute the hash value lazily, but this is not possible if toHash needs to be pure.

Hence my point.  The laziness could be implemented on the compiler side, thereby bypassing the contracts of purity and constancy.

For example, if the source code is

    string toString() const pure {
        return ...;
    }

then the compiler would generate code equivalent to

    bool _has_cached_toString;
    string _cached_toString;

    string toString() {
        if (!_has_cached_toString) {
            _cached_toString = ...;
            _has_cached_toString = true;
        }
        return _cached_toString;
    }

and moreover, clear the _has_cached_toString flag whenever any of the members on which the cached value depends is changed.

> A explicit optional @memoize annotation (similar to the std.functional.memoize) allows
toHash to be both catching and safe. (I was also thinking about the idea of a
@trusted_pure, but I don't like it, and I think it causes chaos).
>
> Bye,
> bearophile

February 16, 2012
On Thu, Feb 16, 2012 at 12:35:20AM -0800, Walter Bright wrote:
> These all need to be:
> 
>     const pure nothrow @safe
> 
> Unless this is done, the utility of const, pure, nothrow and @safe is rather crippled.
> 
> Any reason why they shouldn't be?

Nope.


> One reason is memoization, aka lazy initialization, aka logical const. I don't believe these are worth it. If you must do it inside those functions (and functions that override them), you're on your own to make it work right (use unsafe casts, etc.).

This is a non-problem once the compiler implements memoization as an optimisation. Which it can't until we go ahead with this change. This is the direction that we *should* be going anyway, so why not do it now rather than later?


T

-- 
Latin's a dead language, as dead as can be; it killed off all the Romans, and now it's killing me! -- Schoolboy
February 16, 2012
On Thursday, 16 February 2012 at 08:35:20 UTC, Walter Bright wrote:
> These all need to be:
>
>    const pure nothrow @safe
>
> Unless this is done, the utility of const, pure, nothrow and @safe is rather crippled.
>
> Any reason why they shouldn't be?
>
> One reason is memoization, aka lazy initialization, aka logical const. I don't believe these are worth it. If you must do it inside those functions (and functions that override them), you're on your own to make it work right (use unsafe casts, etc.).

+1
February 16, 2012
Stewart Gordon:

> For example, if the source code is
> 
>      string toString() const pure {
>          return ...;
>      }
> 
> then the compiler would generate code equivalent to
> 
>      bool _has_cached_toString;
>      string _cached_toString;
> 
>      string toString() {
>          if (!_has_cached_toString) {
>              _cached_toString = ...;
>              _has_cached_toString = true;
>          }
>          return _cached_toString;
>      }

The purpose of using an explicit @memoize is to offer the programmer the choice to enable or disable such caching. On default there is no caching.

If toString() is requires only rarely and the string is large but quick to compute, you probably don't wait it to be cached, so you don't add @memoize. If toHash is needed often, and its computation requires some time, given that it only requires one word of memory, you probably want to add @memoize.

The compiler is then free to implement @memoize with a dictionary when there are arguments and with a bool+field when the memoized method has no arguments.

Bye,
bearophile
February 16, 2012
On Thursday, February 16, 2012 09:38:54 H. S. Teoh wrote:
> This is a non-problem once the compiler implements memoization as an optimisation. Which it can't until we go ahead with this change. This is the direction that we *should* be going anyway, so why not do it now rather than later?

I would point out that there are no plans to implement any kind of memoization in the language or compiler. Also, while it can help performance, it can also _harm_ performance. So having it controlled by the compiler is not necessarily a great idea anyway. It's really the sort of thing that should involve profiling on the part of the programmer.

If you want memoization, use std.functional.memoize.

- Jonathan M Davis
February 16, 2012
On Thu, Feb 16, 2012 at 01:53:46PM -0500, Jonathan M Davis wrote:
> On Thursday, February 16, 2012 09:38:54 H. S. Teoh wrote:
> > This is a non-problem once the compiler implements memoization as an optimisation. Which it can't until we go ahead with this change. This is the direction that we *should* be going anyway, so why not do it now rather than later?
> 
> I would point out that there are no plans to implement any kind of memoization in the language or compiler. Also, while it can help performance, it can also _harm_ performance. So having it controlled by the compiler is not necessarily a great idea anyway. It's really the sort of thing that should involve profiling on the part of the programmer.
[...]

Then I agree with bearophile that we should have @memoize (or its
negation), so that the programmer can indicate to the compiler that the
function should be memoized (or not).


T

-- 
It won't be covered in the book. The source code has to be useful for something, after all. -- Larry Wall