June 18, 2012
On Monday, 18 June 2012 at 15:24:31 UTC, Mehrdad wrote:
> On Monday, 18 June 2012 at 15:21:36 UTC, Timon Gehr wrote:
>>> So (**IMHO**) if that's really the case, we should really spend some time fixing the /design/ of const before the implementation...
>>
>> This is mostly about the design of object initialisation.
>>
>>> good idea or no?
>>
>> Certainly.
>
>
> My initial instinct would be to require a "const constructor" in order for an object to be const-able, but I'm not sure if that would work correctly or not..

 Hmmm... Reminds me of an issue that ended up coming up regarding code I was transforming to use immutable rather than mutable data. I kept coming up with the same issues. Without a const specific constructor, the immutable data I was basing the information off refused to go to a mutable temporary (since I needed it to be mutable), even if the end object was immutable and the data was untouched.

 I ended up with having to make a separate function that acted as a constructor, copying immutable elements and data as needed as mutable, then passing it back as immutable afterwards. A little ugly but it got the job done.

 I also came across an interesting problem.

struct X {
    immutable int i_i = 10;
    const int c_i = 10;   //same with immutable

    immutable int i_i2;
    const int c_i2;

    this (int n) {
        i_i = n; //ct error
        c_i = n; //ct error

        i_i2 = n;
        c_i2 = n;
    }
}

void main(){
    X x = X(15);
}

 In this case it refuses to modify i_i & c_i during the constructor, the value of i_i & c_i specify is basically a replacement default(init) and should remain mutable until the constructor is finished (Or at least allowed to set once). If I wanted a strictly unchanged value I probably would have used an enum, and not used the space.

 Is this a bug or intentional?
June 18, 2012
Le 18/06/2012 17:29, Timon Gehr a écrit :
> On 06/18/2012 05:14 PM, Christophe Travert wrote:
>> Matthias Walter , dans le message (digitalmars.D:170036), a écrit :
>>> On 06/18/2012 07:36 AM, Mehrdad wrote:
>>>> Is it just me, or did I subvert the type system here?
>>>>
>>>>
>>>> import std.stdio;
>>>>
>>>> struct Const
>>>> {
>>>> this(void delegate() increment)
>>>> { this.increment = increment; }
>>>> int a;
>>>> void delegate() increment;
>>>> void oops() const { this.increment(); }
>>>> }
>>>>
>>>> void main()
>>>> {
>>>> Const c;
>>>> c = Const({ c.a++; });
>>>> writeln(c.a);
>>>> c.oops();
>>>> writeln(c.a);
>>>> }
>>>>
>>>
>>> I don't think so. When calling oops you have two references to the
>>> object c:
>>>
>>> - The this-pointer of the object itself which is not allowed to change
>>> the object in the const-call.
>>> - The reference from within main which is allowed to change it and can
>>> be reached via the frame pointer of the delegate.
>>>
>>> I see this as perfectly valid code. Of course, opinions may differ here.
>>
>> But here, the frame pointer of the delegate is part of the const
>> structure. By transitivity, the frame pointer should be const, ...
>
> 'By transitivity' is not a sufficient reason. What you really mean is
> 'For the guarantee that a const pure method does not change its mutable
> parameters'.

Transitivity by itself is required to solve a wide range of problem. The most obvious one is controlling what is shared and what isn't using the type system.
June 18, 2012
Le 18/06/2012 09:14, Mehrdad a écrit :
> Okay, how about this? http://ideone.com/VMlzS
>
> Does this break const?
>
>
> import std.stdio;
> class S
> {
> this(int a)
> {
> this.a = a;
> this.increment = { this.a++; };
> }
> int a;
> void delegate() increment;
> void oops() const { this.increment(); }
> }
> void main()
> {
> auto c = new const(S)(0);
> writeln(c.a);
> c.oops();
> writeln(c.a);
> }

Depending on how it is specified, I think you should either :
 - get an error when constructing c, because S isn't « constable ». (delegate type cannot be consted, but can be unconsted safely).
 - get an error when you try to call increment in oops, because the delegate type can't ensure the constness of the operation.
June 18, 2012
On 06/18/12 23:08, deadalnix wrote:
> Le 18/06/2012 09:14, Mehrdad a écrit :
>> Okay, how about this? http://ideone.com/VMlzS
>>
>> Does this break const?
>>
>>
>> import std.stdio;
>> class S
>> {
>> this(int a)
>> {
>> this.a = a;
>> this.increment = { this.a++; };
>> }
>> int a;
>> void delegate() increment;
>> void oops() const { this.increment(); }
>> }
>> void main()
>> {
>> auto c = new const(S)(0);
>> writeln(c.a);
>> c.oops();
>> writeln(c.a);
>> }
> 
> Depending on how it is specified, I think you should either :
>  - get an error when constructing c, because S isn't « constable ». (delegate type cannot be consted, but can be unconsted safely).
>  - get an error when you try to call increment in oops, because the delegate type can't ensure the constness of the operation.
> 

I'm afraid that:

- Banning implicit mutable->const conversions would do far more harm,
  so this would not be a good solution to the problem.
- Requiring that the type of the delegate "ensure the constness" would
  be far too restrictive. (the question would be: "Is any data reachable
  via 'this' also reachable through the delegates context pointer?" and
  every such delegate would of course need to be "pure" [1].

[This isn't *just* about the above example, you get the same problems
 when a const object isn't created but received from somewhere.]

artur

[1] which is a misnomer.
June 19, 2012
Le 19/06/2012 00:16, Artur Skawina a écrit :
> On 06/18/12 23:08, deadalnix wrote:
>> Le 18/06/2012 09:14, Mehrdad a écrit :
>>> Okay, how about this? http://ideone.com/VMlzS
>>>
>>> Does this break const?
>>>
>>>
>>> import std.stdio;
>>> class S
>>> {
>>> this(int a)
>>> {
>>> this.a = a;
>>> this.increment = { this.a++; };
>>> }
>>> int a;
>>> void delegate() increment;
>>> void oops() const { this.increment(); }
>>> }
>>> void main()
>>> {
>>> auto c = new const(S)(0);
>>> writeln(c.a);
>>> c.oops();
>>> writeln(c.a);
>>> }
>>
>> Depending on how it is specified, I think you should either :
>>   - get an error when constructing c, because S isn't « constable ». (delegate type cannot be consted, but can be unconsted safely).
>>   - get an error when you try to call increment in oops, because the delegate type can't ensure the constness of the operation.
>>
>
> I'm afraid that:
>
> - Banning implicit mutable->const conversions would do far more harm,
>    so this would not be a good solution to the problem.
> - Requiring that the type of the delegate "ensure the constness" would
>    be far too restrictive. (the question would be: "Is any data reachable
>    via 'this' also reachable through the delegates context pointer?" and
>    every such delegate would of course need to be "pure" [1].
>
> [This isn't *just* about the above example, you get the same problems
>   when a const object isn't created but received from somewhere.]
>
> artur
>
> [1] which is a misnomer.

Due to D concept of weak purity, this doesn't ensure the required what we need here.

It is possible to get the error when trying to call the delegate instead of preventing to make it const, as I said in the post you quote. It is probably a better solution.
June 19, 2012
On 18/06/12 17:00, Artur Skawina wrote:
> On 06/18/12 16:41, deadalnix wrote:
>> Le 18/06/2012 16:28, Artur Skawina a écrit :
>>> It's fine, if you view a delegate as opaque.
>>>
>>
>> No it isn't. You cannot ensure transitivity anywhere. This have obvious, and severe drawback for concurrent programing (implicit sharing is back) and GC performances (GC can eb crazy fast when it come to transitive immutable data, see OCaml's GC performances for instance).
>>
>>> 'this' being const does not preclude accessing the object that 'this'
>>> points to via another, mutable, reference.
>>>
>>> Consider the alternative - you'd have to forbid storing any delegate
>>> with a non-const non-value argument inside any object.
>
> So how would you like to handle this? And, no, allowing only the cases
> that /can/ be statically checked is not ok - it would result in black
> magic - delegates would be accepted or not depending on the contents
> of the object (think templates and composition).
>
>>> And "breaking" const would then _still_ be possible and trivial.
>>>
>>
>> No, and your example don't demonstrate that in any way. Transitivity is maintained in the example below, because g isn't a member of s, and if it were, then the example would break at compile time.
>
> The word "breaking" is in quotes for a reason. Const is not an immutability
> guarantee. If you treat delegates as opaque then there's no practical
> difference between using them or accessing the data via another external
> reference.
>
>> purity is another beast altogether.
>
> D's "weak pure" can help; I just don't like the redefinition of the term
> "purity"; another name for "weak purity" would be better.

So would I. Can you think of one?
It was the best name I could come up with, given that the 'pure' was the keyword.
We want a word that means 'no hidden state'.
June 19, 2012
On 19 June 2012 09:18, Don Clugston <dac@nospam.com> wrote:
> So would I. Can you think of one?
> It was the best name I could come up with, given that the 'pure' was the
> keyword.
> We want a word that means 'no hidden state'.

I thought that was what pure was for. :~)

-- 
Iain Buclaw

*(p < e ? p++ : p) = (c & 0x0f) + '0';
June 19, 2012
Iain Buclaw , dans le message (digitalmars.D:170145), a écrit :
> On 19 June 2012 09:18, Don Clugston <dac@nospam.com> wrote:
>> So would I. Can you think of one?
>> It was the best name I could come up with, given that the 'pure' was the
>> keyword.
>> We want a word that means 'no hidden state'.
> 
> I thought that was what pure was for. :~)
> 
> -- 
> Iain Buclaw
> 
> *(p < e ? p++ : p) = (c & 0x0f) + '0';


A delegate does have a frame pointer, and it's not that well hidden. If you want no such 'hidden state', you do not want a delegate, you want a function pointer. That means all delegates are weakly pure (until they have an immutable frame pointer qualifier).

If you want that this 'hidden state' does not change, that is another story. pure for a delegate could mean that the frame pointer does not change, but then, pure methods wouldn't allow you to make pure delegates:

struct S
{
  int i;
  int foo() pure { return i; }
}

S s;
int delegate() pure dg = &s.foo;
// error if pure change meaning when applied to a delegate

June 19, 2012
On 19 June 2012 10:47, Christophe Travert <travert@phare.normalesup.org> wrote:
> Iain Buclaw , dans le message (digitalmars.D:170145), a écrit :
>> On 19 June 2012 09:18, Don Clugston <dac@nospam.com> wrote:
>>> So would I. Can you think of one?
>>> It was the best name I could come up with, given that the 'pure' was the
>>> keyword.
>>> We want a word that means 'no hidden state'.
>>
>> I thought that was what pure was for. :~)
>>
>> --
>> Iain Buclaw
>>
>> *(p < e ? p++ : p) = (c & 0x0f) + '0';
>
>
> A delegate does have a frame pointer, and it's not that well hidden. If you want no such 'hidden state', you do not want a delegate, you want a function pointer. That means all delegates are weakly pure (until they have an immutable frame pointer qualifier).
>
> If you want that this 'hidden state' does not change, that is another story. pure for a delegate could mean that the frame pointer does not change, but then, pure methods wouldn't allow you to make pure delegates:
>
> struct S
> {
>  int i;
>  int foo() pure { return i; }
> }
>
> S s;
> int delegate() pure dg = &s.foo;
> // error if pure change meaning when applied to a delegate
>

So we have a few combinations then:

pure  - as in weakly pure, guarantees not to change global state, but may alter it's own hidden state.

pure nothrow - as in strongly pure, where is guaranteed not to have any side effects, so is suitable for constant folding / the usual optimisations for a function typically marked as __pure__ in C.

pure const - similar to strongly pure, as is guaranteed not to be able to alter its own state (as it's const), but still may have side effects / throw an exception.


Make much sense? :-)

-- 
Iain Buclaw

*(p < e ? p++ : p) = (c & 0x0f) + '0';
June 19, 2012
Le 19/06/2012 12:49, Iain Buclaw a écrit :
> So we have a few combinations then:
>
> pure  - as in weakly pure, guarantees not to change global state, but
> may alter it's own hidden state.
>
> pure nothrow - as in strongly pure, where is guaranteed not to have
> any side effects, so is suitable for constant folding / the usual
> optimisations for a function typically marked as __pure__ in C.
>
> pure const - similar to strongly pure, as is guaranteed not to be able
> to alter its own state (as it's const), but still may have side
> effects / throw an exception.
>
>
> Make much sense? :-)
>

You know that it doesn't make any sense, right ?