June 20, 2012
Le 19/06/2012 20:11, Artur Skawina a écrit :
> On 06/19/12 19:30, Christophe Travert wrote:
>> Artur Skawina , dans le message (digitalmars.D:170175), a écrit :
>>> On 06/19/12 15:29, deadalnix wrote:
>>>> Le 19/06/2012 14:30, Artur Skawina a écrit :
>>>>>> Due to D concept of weak purity, this doesn't ensure the required what we need here.
>>>>>
>>>>> Actually, it does - if it can be proved that the delegate can't alter the object
>>>>> via the context pointer (eg because whatever it points to is not mutable) then
>>>>> even D's "pure" is enough. Because the delegate would need to be passed a mutable
>>>>> ref to be able to alter the object, which then could hardly be constructed as
>>>>> "breaking" constness.
>>>>>
>>>>> But such a limit (const/immutable context) would be a problem for the cases where
>>>>> the delegate needs to alter some state (like export the result of some operation),
>>>>> but does _not_ modify the object it's embedded in. Note that the current object may
>>>>> very well be reachable (and mutable) from the delegate - but at some point you have
>>>>> to trust the programmer. Sure, fixing this hole would be great, but /how/ - w/o
>>>>> incurring unacceptable collateral damage?
>>>>>
>>>>
>>>> This isn't a problem as long as the delegate isn't a member of the object. If it is, transitivity is broken, which is something you don't want.
>>>>
>>>> Relying on the trust on the programmer is a dumb idea. Human do mistake, way more than computers. The basic behavior MUST be a safe one.
>>>>
>>>> Transitivity has been introduced for good reason and language already provide a way to break it via cast. So it is unacceptable to rely on programmer on that point.
>>>>
>>>>>> 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.
>>>>>
>>>>> Any delegate?
>>>>>
>>>>
>>>> No, any delegate that have type that isn't covariant with the expected delegate type.
>>>
>>>     struct S {
>>>        int i; this(int i) { this.i = i; }
>>>        T* p;
>>>        void f(int i) { this.i = i; /*p.i++;*/ }
>>>     }
>>>     struct T {
>>>        int i; this(int i) { this.i = i; }
>>>        void delegate(int i) f;
>>>     }
>>>
>>>     void main() {
>>>        auto t = new T(42);
>>>        auto s = new S(17);
>>>        s.p = t;
>>>        t.f =&s.f;
>>>        f(t);
>>>     }
>>>
>>>     void f(const (T)* t) {
>>>        t.f(t.i*2);
>>>     }
>>>
>>> You're proposing to make the last 'f' function illegal, just because the
>>> commented out part could happen. I'm saying that this is unlikely to happen
>>> *by accident*, and of course would still be possible by casting away the
>>> constness.
>>> But banning "unsafe" delegates would result in casts *when using "safe" ones*
>>> - which is not a real improvement because this would make the "bad" casts much
>>> harder to spot.
>>
>>
>> The proper way to do this is not cast, it is to give the proper
>> constness for all types and methods :
>>
>>     struct S {
>>        int i; this(int i) { this.i = i; }
>>        T* p;
>>        void f(int i) const { this.i = i; /*p.i++;*/ }
>> // the commented part is illegal because S.f is const
>
> Not just the commented part.
>
>>     }
>>     struct T {
>>        int i; this(int i) { this.i = i; }
>>        void delegate(int i) const f;
>>    // T.f is const to be callable from .f
>>     }
>>
>>     void main() {
>>        auto t = new T(42);
>>        auto s = new S(17);
>>        s.p = t;
>>        t.f =&s.f; // legal when T.f is const because S.f is also const
>
> Only a const T.f would be pointless.
> Like I've already said twice in this thread - it *can* be done (the
> function has to be "pure" too for it to work), but certain delegate
> uses, which are OK now, would be forbidden.
>

Once again, this is inconsistent with how purity is defined elsewhere.

> I'm all for fixing this hole - it's just that the proposed "fix" would
> have consequences, which can't simply be ignored.
>

They are not ignored, but it seems you don't clearly understand the implications and the big picture.
June 20, 2012
Le 19/06/2012 17:49, Timon Gehr a écrit :
> On 06/18/2012 11:04 PM, deadalnix wrote:
>> 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.
>
> That is completely unrelated.
> It is impossible to justify transitivity of const for delegate context
> pointers using this argument. It is far too general and the
> justification for the general concept comes from a specific example
> that is different from the one at hand.
>
> The question is, what the meaning of 'const' references should be:
>
> 1. data cannot be changed transitively through the reference
>
> 2. the reference can reference both 'const' and 'immutable' data and
> 'immutable' data can transitively not be changed through the
> reference.
>
>
> 1. requires transitive const for delegate context pointers, 2. does not.
>

No, 2. require 1., even if the initialization is broken.

class Foo {
    void delegate() dg;

    this(immutable void delegate() dg) immutable {
        thid.dg = dg;
    }
}

Now, as delegate doesn't carry the constness of its context, an immutable instance of Foo can refers to something that isn't immutable.
June 20, 2012
Artur Skawina , dans le message (digitalmars.D:170191), a écrit :
>> The proper way to do this is not cast, it is to give the proper constness for all types and methods :
>> 
>>    struct S {
>>       int i; this(int i) { this.i = i; }
>>       T* p;
>>       void f(int i) const { this.i = i; /*p.i++;*/ }
>> // the commented part is illegal because S.f is const

I missed that. Then I disagree with your use of delegates. The solution here is to make the free f function to take a non const T*, since the function tries to modify variables from that T instance (via a delegate).

I don't mind if the delegate modify an S or a T instance: it should not modify anything. You use a delegate to store an S structure that you can modifiy in a const T. This is only one step to consider the following acceptable:

struct T {
  ref int delegate() _i;
  this(int i_) { _i = () => i_ }
  @property ref int i() const { return _i(); }
}

void foo(const T*)
{
  T.i = 10;
}

Would you say that this is not a problem, since you do not modify a T instance with that delegate?

I prefer to legalise:
struct T
{
  mutable int i; // makes modifying i legal, but prevent instanciating
                 // an immutable T and several optimizations
}

> Only a const T.f would be pointless.
I would allows you to safely call that delegate from a const T instance.

> Like I've already said twice in this thread - it *can* be done (the function has to be "pure" too for it to work), but certain delegate uses, which are OK now, would be forbidden.
>
> I'm all for fixing this hole - it's just that the proposed "fix" would have consequences, which can't simply be ignored.
> 
> Language design is not a game of whack-a-mole, where you ban random features left and right, because you think you've noticed a problem, without properly identifying it nor spending even a single second figuring out the implications.

I completely agree with that. And I'm fine with the current situation: you trust the programmer not to use delegates to break the const system until we have a proper system to deal with this (inference of constness of delegates, for a start). But encouraging the use of delegates to modify variables from a const instance seems wrong to me.

-- 
Christophe
June 20, 2012
deadalnix , dans le message (digitalmars.D:170262), a écrit :
> Once again, this is inconsistent with how purity is defined elsewhere.
> 
>> I'm all for fixing this hole - it's just that the proposed "fix" would have consequences, which can't simply be ignored.
>>
> 
> They are not ignored, but it seems you don't clearly understand the implications and the big picture.

Let me stress that argument: It has been said in several places in the newsgroup that D had many features that work right, but now problems emerge when feature interacts with each other. That is why consistency in the langage design is *very* important.

Implementing delegates properly with constness of the context pointer, and a consistent pure attribute is a way that garanties that delegates interacts finely with all other feature (constness, purity, etc.): delegates are implementable has classique structures with an opApply like I showed in a post in this thread. There is no black magic (1), an thus delegates are guaranted not add any hole in the langage: delegates are only a very convenient way to do what you can do with a context pointer and a function pointer.

(1) there is still a little bit of magic: closure makes automatic heap allocation of normally stack variables. Since you could do that explicitely, this is no big problem. The only way to make this magic disappear is to make heap allocation of normally stack variables legal in other cases, which would cover another big hole in the langage but this is too big a change in the langage to be accepted for the moment.

-- 
Christophe
June 20, 2012
Le 20/06/2012 10:18, Christophe Travert a écrit :
> deadalnix , dans le message (digitalmars.D:170262), a écrit :
>> Once again, this is inconsistent with how purity is defined elsewhere.
>>
>>> I'm all for fixing this hole - it's just that the proposed "fix" would
>>> have consequences, which can't simply be ignored.
>>>
>>
>> They are not ignored, but it seems you don't clearly understand the
>> implications and the big picture.
>
> Let me stress that argument: It has been said in several places in the
> newsgroup that D had many features that work right, but now problems
> emerge when feature interacts with each other. That is why consistency
> in the langage design is *very* important.
>
> Implementing delegates properly with constness of the context pointer,
> and a consistent pure attribute is a way that garanties that delegates
> interacts finely with all other feature (constness, purity, etc.):
> delegates are implementable has classique structures with an opApply
> like I showed in a post in this thread. There is no black magic (1), an
> thus delegates are guaranted not add any hole in the langage: delegates
> are only a very convenient way to do what you can do with a context
> pointer and a function pointer.
>
> (1) there is still a little bit of magic: closure makes automatic heap
> allocation of normally stack variables. Since you could do that
> explicitely, this is no big problem. The only way to make this magic
> disappear is to make heap allocation of normally stack variables legal
> in other cases, which would cover another big hole in the langage but
> this is too big a change in the langage to be accepted for the moment.
>

What is the conclusion here ?
June 20, 2012
On 19/06/12 11:02, Iain Buclaw wrote:
> 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. :~)

This page is interesting.

http://en.wikipedia.org/wiki/Pure_function

There's never any hidden state, even in a weakly pure function.
So it satisfies rule 1.
A weakly pure function may modify anything it can reach through the parameters, though. Does this violate rule 2?

I guess that if you define all mutable arguments of a weakly pure function as being 'pass by reference', then even 'weakly pure' is pure according to that definition.

The page states that according to that definition, the expressions which call the pure function are not pure.
But again in the terms of that page, D has an "Effect System" which allow us to prove that a function is pure even though it has impure expressions in it.

Wikipedia isn't very authoritative though.

So I don't know -- although D 'pure' is very different to what FP people call 'pure', I'm no longer certain that we're abusing the term.

After all, even this function isn't pure in the traditional FP view:

int foo(int n) pure
{
   int r = n*n;
   r += n;
   return r;
}
June 20, 2012
deadalnix , dans le message (digitalmars.D:170272), a écrit :
> Le 20/06/2012 10:18, Christophe Travert a écrit :
> What is the conclusion here ?

I think it is the same as yours.

My conclusion is that delegate's signature should be the same has methods's signature, and have the same meaning. That makes the langage consistent, and makes sure not to bring any new hole in the langage.

In detail, they should be able to have a const or immutable attribute for their frame pointer, just like methods can be const or immutable with regard to the this argument, and a pure attribute that has the same meaning as methods pure attribute: no global variable access (and no impure function calls), but possibility to access the frame pointer.

-- 
Christophe
June 20, 2012
On 06/20/2012 09:16 AM, deadalnix wrote:
> Le 19/06/2012 17:49, Timon Gehr a écrit :
>>
>> The question is, what the meaning of 'const' references should be:
>>
>> 1. data cannot be changed transitively through the reference
>>
>> 2. the reference can reference both 'const' and 'immutable' data and
>> 'immutable' data can transitively not be changed through the
>> reference.
>>
>>
>> 1. requires transitive const for delegate context pointers, 2. does not.
>>
>
> No, 2. require 1., even if the initialization is broken.
>
> class Foo {
>      void delegate() dg;
>
>      this(immutable void delegate() dg) immutable {
>          thid.dg = dg;
>      }
> }
>
> Now, as delegate doesn't carry the constness of its context, an
> immutable instance of Foo can refers to something that isn't immutable.

Clarification: 'const' means 'const'. No other qualifiers.

There is no 'const' in that example code. 'immutable' obviously needs to be transitive regardless of the particular interpretation of 'const'.

June 20, 2012
Timon Gehr , dans le message (digitalmars.D:170288), a écrit :
> On 06/20/2012 09:16 AM, deadalnix wrote:
>> Le 19/06/2012 17:49, Timon Gehr a écrit :
>>>
>>> The question is, what the meaning of 'const' references should be:
>>>
>>> 1. data cannot be changed transitively through the reference
>>>
>>> 2. the reference can reference both 'const' and 'immutable' data and 'immutable' data can transitively not be changed through the reference.
>>>
>>>
>>> 1. requires transitive const for delegate context pointers, 2. does not.
>>>
>>
>> No, 2. require 1., even if the initialization is broken.
>>
>> class Foo {
>>      void delegate() dg;
>>
>>      this(immutable void delegate() dg) immutable {
>>          thid.dg = dg;
>>      }
>> }
>>
>> Now, as delegate doesn't carry the constness of its context, an immutable instance of Foo can refers to something that isn't immutable.
> 
> Clarification: 'const' means 'const'. No other qualifiers.
>
> There is no 'const' in that example code. 'immutable' obviously needs to be transitive regardless of the particular interpretation of 'const'.
> 
const means: maybe immutable. Thus const needs to be transitive too. If you apply different rules to const and to immutable, you are breaking the consistency of the langage.

June 20, 2012
On 06/20/2012 01:36 PM, Christophe Travert wrote:
> Timon Gehr , dans le message (digitalmars.D:170288), a écrit :
>> On 06/20/2012 09:16 AM, deadalnix wrote:
>>> Le 19/06/2012 17:49, Timon Gehr a écrit :
>>>>
>>>> The question is, what the meaning of 'const' references should be:
>>>>
>>>> 1. data cannot be changed transitively through the reference
>>>>
>>>> 2. the reference can reference both 'const' and 'immutable' data and
>>>> 'immutable' data can transitively not be changed through the
>>>> reference.
>>>>
>>>>
>>>> 1. requires transitive const for delegate context pointers, 2. does not.
>>>>
>>>
>>> No, 2. require 1., even if the initialization is broken.
>>>
>>> class Foo {
>>>       void delegate() dg;
>>>
>>>       this(immutable void delegate() dg) immutable {
>>>           thid.dg = dg;
>>>       }
>>> }
>>>
>>> Now, as delegate doesn't carry the constness of its context, an
>>> immutable instance of Foo can refers to something that isn't immutable.
>>
>> Clarification: 'const' means 'const'. No other qualifiers.
>>
>> There is no 'const' in that example code. 'immutable' obviously needs to
>> be transitive regardless of the particular interpretation of 'const'.
>>
> const means: maybe immutable.

Or maybe mutable. Therefore, interpretation '2.'

> Thus const needs to be transitive too.

Wrong. This is the (A==>B) ==> (B==>A) fallacy, where

A: 'const' is transitive
B: 'const' references cannot modify 'immutable' data

The conclusion regarding transitivity, given interpretation 2., is that
'const' needs to be transitive _if_ it is actually 'immutable'.

> If you apply different rules to const and to immutable, you are breaking
> the consistency of the langage.
>

Certainly not. This is like saying that applying different rules to
'const' and mutable is breaking the consistency of the language.
mutable is not transitive.