May 29, 2013
On 05/28/2013 11:13 PM, Jonathan M Davis wrote:

> On Tuesday, May 28, 2013 22:58:39 Ali Çehreli wrote:

> To make matters worse, what happens when you have a const object which has a
> reference as a member variable? e.g.
>
> class C
> {
>      class D d;
> }
>
> const C c;

Thanks for doing the thinking for me. :) I could not think deeper than one level yesterday. :)

> Because of the transitivity of const, d must be fully const, and yet with your
> suggestion, it isn't.

We know the phrase "turtles all the way down". It makes sense. The interesting thing is, in the case of a class reference, at least due to syntax, it feels like it is "turtle one level up":

    const(C) c = new const(C);

The reason is, the syntax const(C) suggests that the object is const. (Compared to 'const C c = ...') However, although it looks like const qualifiying the object, it comes one step up affects the reference as well.

I wondered whether there were more turtles up: :)

void main()
{
    class A
    {
        int i;
        this(int i) { this.i = i; }
    }

    const(A) a1 = new const(A)(1);
    const(A) a2 = new const(A)(2);

    // The following is a compilation ERROR because it is not
    // possible to refer to another object in a non-mutating way:

    // a1 = a2;  <-- compilation ERROR

    // No problem at all! Define a wrapper for a const(A):
    class B
    {
        const(A) a;
        this(const(A) a) { this.a = a; }
    }

    B b1 = new B(a1);
    B b2 = new B(a2);

    // And use your const(A) through that wrapper:
    assert(b1.a.i == 1);

    // Maybe we should call this wrapper Rebindable ;)
    b1 = b2;

    // Yay! :)
    assert(b1.a.i == 2);
}

I always fail to remember std.typecons.Rebindable. :( It may be the solution to OP's issue:

  http://dlang.org/phobos/std_typecons.html#.Rebindable

Ali

May 29, 2013
On 05/28/2013 08:43 PM, Peter Williams wrote:

> One place I'm using it is for managing symbols (look ahead sets, etc) in
> a parser generator.  The symbols' permanent home is in an associative
> array indexed by their name (which is also included in the symbol
> object) but they may belong to many look ahead sets.

Have you considered Rebindable?

import std.typecons;

class C
{
    void mutate() {}
    void readOnly() const {}
}

void main()
{
    // Here is the actual data:
    C[string] mutableData;
    mutableData["one"] = new C();
    mutableData["two"] = new C();

    // Mutable; good...
    mutableData["one"].mutate();

    alias ConstRef = Rebindable!(const C);

    // Here is a const view of mutable data
    ConstRef[] set;
    set ~= ConstRef(mutableData["one"]);
    set ~= ConstRef(mutableData["two"]);

    set[0].readOnly();
    // set[0].mutate();  <-- compilation error; good...
}

Ali

May 29, 2013
On 2013-05-29 16:02:58 +0000, "Daniel Murphy" <yebblies@nospamgmail.com> said:

> Introduce *C (syntax not important) to give you the raw class type, much like the raw function type.  You can then apply const directly to this type, and an appropriate suffix gets you back to the reference.

Are you sure you're not starting from the wrong assumption? There's no such thing as a "raw class type" in the compiler that is separate from a "class reference".

> This should reduce the compiler changes required, as I recall much of the complexity was due to changing the meaning of the existing type.

To implement what you want you'd have to separate the current class type in two types… which would change pretty much everything related to classes in the compiler.

My technique for "const(Object)ref" was to minimize those changes. What I ended up adding is a way for a type to have head modifiers different from its regular modifiers (but only classes are allowed to have different head modifiers). Then I went on a hunt for places checking modifiers in the compiler (code such as `c->mod`) and convert them to use head modifiers instead (`c->head()->mod`) where it made sense. It took some time, but it's not really difficult once you figure it out.

> This would also play better with template argument deduction, as there was no clear way to define it when ref was optional. The inconsistent handling of arrays and pointers has since been fixed (eg const(T*) matching const(U*), U becomes const(T)* and the same for arrays) so there is a clear pattern to follow.

What was problematic for template argument deduction was the lack of a coherent example of how it should work -- which as you said has been fixed since -- not the optionality of ref.


-- 
Michel Fortin
michel.fortin@michelf.ca
http://michelf.ca/

May 29, 2013
On 29/05/13 19:40, monarch_dodra wrote:
>
> The problem is that you are missing a FUNDAMENTAL difference between C++
> and D. C++'s const merely says: "Though shalt not modify this value",
> whereas D's means "This object's value WILL remain constant, until the
> end of time, and under no circumstance can it ever be modified. Oh. And
> so will everything it references".

More likely C constness but yes I am still learning D.  I suspect that a lot of my D code currently looks like C code using D syntax.  As I learn more I'll go back and change the code to be more D like than C like (especially as this usually leads to major simplifications).

I'm also starting to suspect that my understanding of how C arrays are passed as arguments to functions is affecting my desire to use const when passing them in and this may be misguided.  I've been trying to find out how non ref array arguments are passed to functions in D but can't find any documentation on it.  If doing that is not much less efficient than passing by ref (and isolates the external representation of the array from anything I do) then I can stop using ref and the problem goes away.

Peter
May 29, 2013
On 30/05/13 02:17, Ali Çehreli wrote:
> On 05/28/2013 08:43 PM, Peter Williams wrote:
>
>  > One place I'm using it is for managing symbols (look ahead sets, etc) in
>  > a parser generator.  The symbols' permanent home is in an associative
>  > array indexed by their name (which is also included in the symbol
>  > object) but they may belong to many look ahead sets.
>
> Have you considered Rebindable?
>

No. Never heard of it before :-)

But, having now read the documentation, I don't think it meets my needs.

We need a book on the standard library.

Thanks
Peter

May 29, 2013
On 05/29/2013 03:59 PM, Peter Williams wrote:

> I've been trying to find out how non ref array arguments are passed to
> functions in D but can't find any documentation on it.

The following concepts are relevant:

- Dynamic array: Maintained by the D runtime

- Fixed-length array (aka static array): Can be on the stack

- Slice: An efficient tool to access a range of elements (of any type of array)

Usually, it is the slice that gets passed:

  void foo(int[] slice);

A slice is made up of the pointer to the first element and the number of elements:

struct __SomeImplementationDependentName__
{
    size_t length;
    void * ptr;
}

When you pass a slice by-value, as in the case of foo() above, that struct gets copied: a copy of the argument...

So, slice variables have value semantics but they are used as references to elements.

Fixed-length arrays are a different story: Unlike C arrays and unlike D slices, the elements are always copied.

> If doing that is not much less efficient than passing by ref (and isolates
> the external representation of the array from anything I do) then I can stop
> using ref and the problem goes away.

Yes, simply pass-by-reference. Not expensive at all. There may be surprises though; you may want to read this article:

  http://dlang.org/d-array-article.html

Ali

May 29, 2013
On 05/29/2013 04:45 PM, Ali Çehreli wrote:

> Yes, simply pass-by-reference. Not expensive at all.

Arggh! It should say "simply pass-by-value".

Ali

May 30, 2013
On 30/05/13 09:45, Ali Çehreli wrote:
> Yes, simply pass-by-reference. Not expensive at all. There may be
> surprises though; you may want to read this article:
>
>    http://dlang.org/d-array-article.html

Very informative article.

Thanks
Peter
May 30, 2013
On 30/05/13 10:49, Peter Williams wrote:
> On 30/05/13 09:45, Ali Çehreli wrote:
>> Yes, simply pass-by-reference. Not expensive at all. There may be
>> surprises though; you may want to read this article:
>>
>>    http://dlang.org/d-array-article.html
>
> Very informative article.

Thinking about this some more, it seems I still need the const even with pass by value to reassure the caller that his array won't be altered. So the problem doesn't go away it just changes slightly.

I find the mechanism described in the article a little disconcerting and it certainly needs more publicity as it's a bug in waiting for the unwary. Wouldn't a better rule for pass by value be that any changes to the data part of the array (including assignment to an element) causes reallocation of the entire data portion.

Peter

May 30, 2013
On Wednesday, 29 May 2013 at 23:45:04 UTC, Ali Çehreli wrote:
> On 05/29/2013 03:59 PM, Peter Williams wrote:
>
> > I've been trying to find out how non ref array arguments are
> passed to
> > functions in D but can't find any documentation on it.
>
> The following concepts are relevant:
>
> - Dynamic array: Maintained by the D runtime

Generally yes, but not always http://dpaste.dzfl.pl/ffbcb449

> - Fixed-length array (aka static array): Can be on the stack
>
> - Slice: An efficient tool to access a range of elements (of any type of array)
>
> Usually, it is the slice that gets passed:
>
>   void foo(int[] slice);

Isn't it a dynamic array? I don't understand listing slice as separate type of arrays or mixing meaning of slice and dynamic array. As far as D spec is concerned, slice is a SliceExpression which produces dynamic array for array types.

> A slice is made up of the pointer to the first element and the number of elements:
>
> struct __SomeImplementationDependentName__
> {
>     size_t length;
>     void * ptr;
> }
>
> Ali