With the latest DMD versions it's easy to create an immutable array of structs, 
with a pure function (I think this gives advantages similar to the Transients 
of the Clojure language):


struct Foo {
    int x;
    bool b;
}
immutable(Foo[]) generate() pure nothrow {
    auto arr = new Foo[10];
    foreach (i; 0 .. 10)
        arr[i].x = i;
    arr[3] = Foo(3, true);
    return arr;
}
void main() {
    auto a = generate();
}



But _very_ often I have to create arrays of structs that have mixed 
mutable/immutable fields. To create them I have to use append, that is OK for 
little arrays, but it's not nice when the arrays get large and when the rules 
to fill them become complex:


struct Foo {
    int x;
    immutable bool b;
}
Foo[] generate() pure nothrow {
    Foo[] arr;
    foreach (i; 0 .. 10) {
        if (i == 3) // the rule to fill arr
            arr ~= Foo(i, true);
        else
            arr ~= Foo(i, false);
    }
    return arr;
}
void main() {
    auto a = generate();
}



As alternative, to avoid the append, I am able to use read-only properties for 
the immutable fields, but this requires a more complex struct (and I think 
currently DMD can't perform const-related optimizations on fileds like this 
b__, because in D inside a module the "private" attribute is ignored. I think 
this makes certain compiler optimizations harder, that are instead easy in C# 
because "private" works at module level too, so a read-only property is similar 
to a const value. I'd like to know more about this optimization-related 
situation):


struct Foo {
    int x;
    private bool b__ = false;
    // readonly
    @property bool b() const pure nothrow { return b__; }
    this(in int x_, in bool b_=false) pure nothrow {
        this.x = x_;
        this.b__ = b_;
    }
}
Foo[] generate() pure nothrow {
    auto arr = new Foo[10];
    foreach (i; 0 .. 10)
        arr[i].x = i;    
    arr[3] = Foo(3, true);
    return arr;
}
void main() {
    auto a = generate();
}




This kind of code (currently the type system raises an error here) is not safe 
even using just "const" for the field "b", so it's not a solution, because 
inside generate the const is broken/ignored, so the compiler must perform zero 
const-related optimizations regarding the field b (and its sub-tree data):


struct Foo {
    int x;
    const bool b;
}
Foo[] generate() pure nothrow {
    auto arr = new Foo[10];
    foreach (i; 0 .. 10)
        arr[i].x = i;
    arr[3] = Foo(3, true);
    return arr;
}
void main() {
    auto a = generate();
}



I think code like this is a mess (this is kind of the opposite of the "nested 
constantness" I discussed some time ago):


struct Foo {
    int x;
    const bool b;
}
Foo[] generate() pure nothrow {
    with (mutable Foo.b) {
        auto arr = new Foo[10];
        foreach (i; 0 .. 10)
            arr[i].x = i;
        arr[3] = Foo(3, true);
    }
    return arr;
}
void main() {
    auto a = generate();
}


At the moment I don't have further ideas on this.

Bye,
bearophile

Reply via email to