May 16, 2012
On 05/15/2012 08:18 PM, Chris Cain wrote:
> On Tuesday, 15 May 2012 at 23:36:38 UTC, Chad J wrote:
>> The idea /was/ to store the @instance variable with the object
>> specifically to avoid complex indirections. Still have to trust
>> programmers though :/
>
> But you /can't/ store the @instance variable with the object. As per the
> language reference, immutables may be stored in ROM. If the object is
> immutable (and const objects might be immutable), then the @instance
> variable would be stored in ROM, in which case it physically couldn't
> change no matter how hard you tried (or it would vomit run-time errors
> or some undefined behavior). The only "workable" solution would be an
> immutable pointer to the mutable variable.
>
> Link: http://dlang.org/const3.html
>

I guess this matters for toHash... so now we not only want pure/const/nothrow methods, but there is this implication that we are desiring to mutate immutable things.  If we really wanted to overcome this, we could have things that behave as if immutable, but carry mutable state internally.  These would not be eligible for placement in ROM.

The other thing that I'm wondering about is if there are use-cases /besides/ hashing.  Are there any?  If not, it might be worth special-casing somehow.  It's a difficult route to travel though, because it's very difficult to know that there won't be any roadblocks besides caching in the future.  Still, I suspect this is not well researched.
May 16, 2012
Chad J , dans le message (digitalmars.D:167472), a écrit :
> On 05/15/2012 02:49 PM, Christophe wrote:
>> Chad J , dans le message (digitalmars.D:167461), a écrit :
>>> An idea I thought of is to introduce a method local declaration that allows a method to access instance-specific-state that isn't accessible to the rest of the class:
>>
>> It is not good for caching results, since the cache often has to be erased when the object is modified.
> 
> I did outline a way to invalidate caches with this.

I was a bit fast on my post, but it cannot work: Something has to be modified both normally by a non-const function to invalidate the cache, and exceptionally by the const function to set the cache.


Two remarks about people trying to solve the const problem:

 - People trying to modify a const value by using holes like delegates
and inner classes might as well cast away const.

 - People trying to associate mutable members to a const object might as
well ask for a @mutable keyword to have members that stays mutable even
if the object is const (and remove power to class with @mutable members,
such as the possibility to be immutable, have strongly pure methods,
etc.). It will have the same effect as other proposed work arround, but
at least it is easy to understand how it works and won't cripple the
langage with too many awkward rules.

After all, D is a mutli-paradigm programming langage. It should not force programmers to use hard constness. Better use as clean as possible @mutable data than complicated workarrounds.

May 16, 2012
On 05/16/2012 10:17 AM, Christophe wrote:
> [...] @mutable keyword to have members that stays mutable even
> if the object is const (and remove power to class with @mutable members,
> such as the possibility to be immutable, have strongly pure methods,
> etc.). It will have the same effect as other proposed work arround, but
> at least it is easy to understand how it works and won't cripple the
> langage with too many awkward rules.
>

I think this is a very sane proposal, (the important points are stated in parens) because 'const' alone does not give any useful guarantees. The essential guarantee is that an immutable object attached to a const reference cannot be mutated through it.

An issue is that fully 'const pure' methods would lose their guarantees when passed a mutable object, unless const member functions that mutate @mutable members of the receiver object cannot be pure. This rule would disqualify the solution for what is discussed in the OP.

Leaving the rule out would imply that the currently valid code transformation:

int foo(const pure A){ }

A a = ...;

int x=foo(a), y=foo(a)
=>
int x=foo(a), y=x;

would become incorrect in the general case. The proposal trades off 'const' guarantees against mutable/immutable interoperability. I would be willing to take that.
May 16, 2012
Timon Gehr , dans le message (digitalmars.D:167544), a écrit :
> Leaving the rule out would imply that the currently valid code transformation:
> 
> int foo(const pure A){ }
> 
> A a = ...;
> 
> int x=foo(a), y=foo(a)
> =>
> int x=foo(a), y=x;
> 
> would become incorrect in the general case. The proposal trades off 'const' guarantees against mutable/immutable interoperability. I would be willing to take that.

The language could declare that the transformation is legal, and that the programmer using 'mutable' members is responsible for keeping the function logically const.

-- 
Christophe
May 17, 2012
Am Mon, 14 May 2012 16:54:34 -0700
schrieb Walter Bright <newshound2@digitalmars.com>:

> On 5/14/2012 10:08 AM, Tove wrote:
> > but c++ has the 'mutable' keyword as an easy escape route...
> 
> The existence of that capability means that 'const' in C++ cannot be meaningfully reasoned about.

class Foo
{
  uint a, b;
  // can only call const pure nothrow members here:
  lazy uint somethingLazyInitialized = { return 2 * bar(); }

  uint bar() const pure nothrow @safe
  {
    // complex calculation
    return a + b;
  }

  override uint toHash() const pure nothrow @safe
  {
    // sets the field on the first use
    return somethingLazyInitialized;
  }
}

Internally the "lazy uint" consists of a function pointer and the uint. A lazy field acts like a read-only property. Whenever the field is read, code is generated that first checks, if the function pointer is not null. It then updates the uint with the return value of the function call and sets the function pointer to null, to indicate that the value is now initialized.

An instance of Foo cannot be immutable (it makes no sense to ask for that with lazy initialization), but it can be const. This is a form of logical const that still allows reasoning about the code + compiler enforcement in contrast to the more flexible (in a positive and negative sense) C++ mutable.

-- 
Marco
May 23, 2012
On Tuesday, 15 May 2012 at 21:18:11 UTC, Chad J wrote:
> On 05/15/2012 03:32 PM, Chris Cain wrote:
>> On Tuesday, 15 May 2012 at 18:07:12 UTC, Chad J wrote:
>>> An idea I thought of is to introduce a method local declaration that
>>> allows a method to access instance-specific-state that isn't
>>> accessible to the rest of the class:
>>
>> This is an interesting idea (as it seems to really try to keep
>> the state changes internal to the function, which can be seen  as
>> how D handles purity)... however, it breaks the point of purity due to
>> this:
>>
>> pure nothrow hash_t toHash() const {
>> @instance hash_t bad = 0;
>> ++bad;
>> return hashfn(field) + bad;
>> }
>>
>> Now it violates everyone's definition of purity and we can no
>> longer make our sweet optimizations and reasoning about the  code.
>> Sure, we could "trust the programmers to not do this" ... but
>> that's another debate entirely.
>
>
> Yes.  I intend to "trust the programmers to not do this".
>
> Otherwise we need to find some way to ensure that a function that alters external state will always return the same value as long as the rest of the program doesn't change the state it looks at.
>
>>
>> ... and setting toStringCacheValid to true in toString violates
>> const, so this is absolutely not allowed. Sorry.
>>
>
> Yep, ya got me.
>
>>
>> Maybe there's a solution, but I doubt the solution is something
>> the programmer can/should do completely transparently.

 I think I have an answer. It came to me last night when I was going to sleep. Anyways, it may be a little verbose but I Hope I got it all right. Oddly enough it stays within D's own rules :)

--

import std.stdio;
import std.conv;

struct CachedHash(T) {
  T element;
  uint hash;
  alias element this;

  this(T inVal) {
    element = inVal;
  }

  @property uint toHash(){
    if(!hash) {
      writeln("Hashing!");
      hash = element.toHash();
    } else
      writeln("From Cache!");
    return hash;
  }
}

uint toHash(string x){
  return 123456; //for example
}

class AString {
  string str;

  this(string s) {
    str = s;
  }

  const uint toHash(){
    return 123456;
  }
}

uint calledHash(T)(T as){
  return as.toHash();
}

int main() {
  immutable char[] s = "this is a test!";

  auto ch_s = CachedHash!string(s);  //since s is inherently immutable..

  writeln(s);
  writeln(s.toHash, "\n");

  writeln(ch_s);
  writeln(ch_s.toHash);
  writeln(ch_s.toHash, "\n");

  AString as = new AString(s);
  immutable AString ias = cast(immutable AString) new AString(s);

  auto as_s = CachedHash!AString(as);
  auto ias_s = CachedHash!(immutable AString)(ias);

  writeln(as.toHash, "\n");
  writeln(as_s.toHash);
  writeln(as_s.toHash);
  writeln(ias_s.toHash);
  writeln(ias_s.toHash, "\n");

  writeln(calledHash!AString(as_s)); //non-CachedHash aware simulate
  writeln(calledHash!(typeof(as_s))(as_s));

  return 0;
}


1 2 3 4 5 6 7 8 9 10
Next ›   Last »