May 16, 2006
Walter Bright wrote:
> Sean Kelly wrote:
> 
>> Exactly.  And this was the motivation for my suggestion above.  If there is no way to prevent the user from subverting a system built upon reference attributes, then the only alternative I can think of would be to build it upon the data itself.  D already has the const storage type which does much the same thing: if the user manages to outfox the compiler and modify data in ROM an error will occur.  A similar measure of protection could be extended to dynamic data using the page protection features supplied by the memory manager, but there are some obvious problems:
>>
>> - To add data to a protected page the page must either be temporarily unprotected, making it vulnerable to modification by another thread, or it must be modified and the resulting error ignored (which is quite slow).
>>
>> - Such protection is far from granular, as it requires copying data the compiler cannot guarantee will not be modified by user code into a protected memory page.  But as far as I'm aware, there is no other way to obtain low-level support for read-only data.
>>
>> I can think of a few possible workarounds to the above problems, but all of them would require special handing of references to const data, and this seems unacceptable for a language like D.  But a data-oriented const system seems the only option for something that could actually be exploited by an optimizer.  It's just too easy to work around any such attempt built into the language syntax itself.  Heck, inline assembler is an obvious and huge back-door to any syntax-oriented protection mechanism.  There's simply no point in even considering that route.
> 
> 
> I have been thinking along the lines of making 'const' a promise *by the programmer* not to modify the data, and then the compiler will *assume* the promise is kept. 


Is there a way you can do this whereby the source-level "tagging" of these promises is explicit enough for a lint-like tool to operate upon in a (fully) deterministic manner?

I ask because there seems to be two opposing needs involved to support the notion of 'const': the complexity and performance of the compiler, and the facility for a programmer to be informed when they invariably make a mistake. As language users, we tend to rely upon the latter?
May 16, 2006
kris wrote:
> Walter Bright wrote:
>> Sean Kelly wrote:
>>
>>> Exactly.  And this was the motivation for my suggestion above.  If there is no way to prevent the user from subverting a system built upon reference attributes, then the only alternative I can think of would be to build it upon the data itself.  D already has the const storage type which does much the same thing: if the user manages to outfox the compiler and modify data in ROM an error will occur.  A similar measure of protection could be extended to dynamic data using the page protection features supplied by the memory manager, but there are some obvious problems:
>>>
>>> - To add data to a protected page the page must either be temporarily unprotected, making it vulnerable to modification by another thread, or it must be modified and the resulting error ignored (which is quite slow).
>>>
>>> - Such protection is far from granular, as it requires copying data the compiler cannot guarantee will not be modified by user code into a protected memory page.  But as far as I'm aware, there is no other way to obtain low-level support for read-only data.
>>>
>>> I can think of a few possible workarounds to the above problems, but all of them would require special handing of references to const data, and this seems unacceptable for a language like D.  But a data-oriented const system seems the only option for something that could actually be exploited by an optimizer.  It's just too easy to work around any such attempt built into the language syntax itself.  Heck, inline assembler is an obvious and huge back-door to any syntax-oriented protection mechanism.  There's simply no point in even considering that route.
>>
>>
>> I have been thinking along the lines of making 'const' a promise *by the programmer* not to modify the data, and then the compiler will *assume* the promise is kept. 
> 
> 
> Is there a way you can do this whereby the source-level "tagging" of these promises is explicit enough for a lint-like tool to operate upon in a (fully) deterministic manner?
> 
> I ask because there seems to be two opposing needs involved to support the notion of 'const': the complexity and performance of the compiler, and the facility for a programmer to be informed when they invariably make a mistake. As language users, we tend to rely upon the latter?

What I think most programmers would settle for is that const would be enforced in the 'easy' cases like:

int foo(const SomeObject o, const int[] arr)
{
    o.i = 20;     // error, is not an lvalue
    arr[10] = 30; // error, is not an lvalue
    o.bar();      // Ok
    o.baz();      // error, discards qualifiers
    o = new SomeObject; // Ok
//  ...
}

class SomeObject
{
    int i;
    int bar() const // a promise object isn't modified (like C++)
    {
        //i = 30; error, i is not an lvalue
        return i; // Ok
    }
    void baz()
    {
        i = 40;
    }
}

The compiler would just check that a member of any type of aggregate/object cannot be on the LHS of any assignment operator, or that a call to an object method is const, that's it.

If they modify a const param. through the reference, then all bets are off.

To reinforce the idea of const only for aggregates/objects, and to make the implementation as simple as possible, don't allow naked pointer variables to be const if that avoids a significant amount of complexity.

Then write the doc. (and spec.) to reflect this, so what const is expected to do is not ambiguous for either programmers or compiler developers.

Just a thought...

- Dave
May 16, 2006
Walter Bright wrote:
> 
> I have been thinking along the lines of making 'const' a promise *by the programmer* not to modify the data, and then the compiler will *assume* the promise is kept.

How would the programmer know whether data was being modified?  For example:

    class MyClass {
        char[] name() {
            if( !m_name )
                m_name = readNameFromDB();
            return m_name;
        }
    }

    const MyClass ref = new MyClass;
    printf( "%.*s\n", ref.name );

It seems this could impose maintenance problems and may require code the user wants to const-qualify to be inspectable.  Or are you suggesting there would be some language support for checking this?  And if so, how would you avoid a Cpp-like syntax?

> Your idea is a way to enforce the issue. The
> problem is that changing page protection attributes is very slow - but one could afford the cost in "debug compiles."
>
> This would be a cool way to implement "const-correctness" without the hassle and cruft. There are still problems, though. You can't make part of an object readonly with hardware.

Yeah it's kind of a sledgehammer for delicate work.  But it might prove useful for tracking down some const violations.  I may experiment with it from a library perspective and see if I can find a usable syntax.


Sean
May 24, 2006
why not make a feature:

    safe foreach( Person p; ....)


"Walter Bright" <newshound@digitalmars.com> дÈëÏûÏ¢ÐÂÎÅ:e3vq3l$1rp5$3@digitaldaemon.com...
> Sean Kelly wrote:
>> Very cool.  One thing... does this work:
>>
>>     foreach( Person p; per.each )
>>     {
>>         if( p.age > 50 )
>>             p.listRemove();
>>     }
>>
>> ie. can you remove elements within a foreach?
>
> It's undefined behavior. foreach is entitled to assume that the aggregate is a loop invariant, although the contents of the aggregate's elements can change. This precludes things like resizing an array inside a foreach, etc.


May 24, 2006
Boris Wang wrote: [edited upside-down post]
> "Walter Bright" <newshound@digitalmars.com> дÈëÏûÏ¢ÐÂÎÅ:e3vq3l$1rp5$3@digitaldaemon.com...
>> Sean Kelly wrote:
>>> Very cool.  One thing... does this work:
>>>
>>>     foreach( Person p; per.each )
>>>     {
>>>         if( p.age > 50 )
>>>             p.listRemove();
>>>     }
>>>
>>> ie. can you remove elements within a foreach?
>> It's undefined behavior. foreach is entitled to assume that the aggregate is a loop invariant, although the contents of the aggregate's elements can change. This precludes things like resizing an array inside a foreach, etc. 
> 
> why not make a feature:
>
>     safe foreach( Person p; ....)

If such a feature was to be implemented, I can think of a better (cleaner and more consistent with the rest of the language) syntax:

    foreach(Peron p; inout people)
    {
        // ...
    }

Why invent a new keyword when an old one (though in a different place) perfectly expresses what's going on?

	Frits van Bommel
May 24, 2006
On Wed, 24 May 2006 08:47:21 +0200, Frits van Bommel wrote:

> Boris Wang wrote: [edited upside-down post]
>> "Walter Bright" <newshound@digitalmars.com> дÈëÏûÏ¢ÐÂÎÅ:e3vq3l$1rp5$3@digitaldaemon.com...
>>> Sean Kelly wrote:
>>>> Very cool.  One thing... does this work:
>>>>
>>>>     foreach( Person p; per.each )
>>>>     {
>>>>         if( p.age > 50 )
>>>>             p.listRemove();
>>>>     }
>>>>
>>>> ie. can you remove elements within a foreach?
>>> It's undefined behavior. foreach is entitled to assume that the aggregate is a loop invariant, although the contents of the aggregate's elements can change. This precludes things like resizing an array inside a foreach, etc.
>> 
>  > why not make a feature:
>  >
>  >     safe foreach( Person p; ....)
> 
> If such a feature was to be implemented, I can think of a better (cleaner and more consistent with the rest of the language) syntax:
> 
>      foreach(Peron p; inout people)
>      {
>          // ...
>      }
> 
> Why invent a new keyword when an old one (though in a different place) perfectly expresses what's going on?

Yes!

The meaning of this would be that "people" would be 're-evaluated' prior to each iteration under the assumption that something in the body of the loop modified the array in some manner.

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Down with mediocracy!"
24/05/2006 5:07:14 PM
May 24, 2006
Derek Parnell skrev:
> On Wed, 24 May 2006 08:47:21 +0200, Frits van Bommel wrote:
> 
>> Boris Wang wrote: [edited upside-down post]
>>> "Walter Bright" <newshound@digitalmars.com> дÈëÏûÏ¢ÐÂÎÅ:e3vq3l$1rp5$3@digitaldaemon.com...
>>>> Sean Kelly wrote:
>>>>> Very cool.  One thing... does this work:
>>>>>
>>>>>     foreach( Person p; per.each )
>>>>>     {
>>>>>         if( p.age > 50 )
>>>>>             p.listRemove();
>>>>>     }
>>>>>
>>>>> ie. can you remove elements within a foreach?
>>>> It's undefined behavior. foreach is entitled to assume that the aggregate is a loop invariant, although the contents of the aggregate's elements can change. This precludes things like resizing an array inside a foreach, etc. 
>>  > why not make a feature:
>>  >
>>  >     safe foreach( Person p; ....)
>>
>> If such a feature was to be implemented, I can think of a better (cleaner and more consistent with the rest of the language) syntax:
>>
>>      foreach(Peron p; inout people)
>>      {
>>          // ...
>>      }
>>
>> Why invent a new keyword when an old one (though in a different place) perfectly expresses what's going on?
> 
> Yes! 
> 
> The meaning of this would be that "people" would be 're-evaluated' prior to
> each iteration under the assumption that something in the body of the loop
> modified the array in some manner.

By reevaluation, I presume you don't mean that given:

foreach(var; inout Expression),

Expression is reevaluated on each iteration?

What would the semantics be for iteration over a regular array when the array is applied the different changing operations?

I.e. what should foreach do if the following is done inside the loop body:

* arr ~= element;
* arr = arr[0..i-1] ~ arr[i+1..$]; // (legal?)
* arr[i] = arr[$-1], arr.length = arr.length-1;
* arr = arr[-1..$];
* arr = otherArr ~ arr;
* more?

I guess the only feasible equivalent of

foreach(x ; inout arr())

would be:

{ auto array = arr();
  for (int i = 0; i < array.length; i++) {
    /*inout*/ auto x = array[i];

But that goes against the "each" in foreach by skipping the next element following a deletion and iterating doubly over elements if the array is appended to while the loop is running.

/Oskar
1 2 3 4
Next ›   Last »