Sorry for my answers coming so slowly.

Mason McGill:

Is this considered idiomatic?

Time ago I have asked here to make this index 'i' immutable on default, because the literal "0 .. 10" defines an immutable range of values:

void main() {
    import std.stdio;
    foreach (i; 0 .. 10) {
        writeln(i);
    }
}


Originally code like this was giving weird results, but thanks to Kenji this design bug was fixed, and now this code prints the first ten integers normally, so now the situation is not as bad as it used to be:

void main() {
    import std.stdio;
    foreach (i; 0 .. 10) {
        writeln(i);
        i++;
    }
}



Currently this prints just the even numbers, I think this is a little weird, but I think it sufficiently safe thanks to the explicit 'ref' annotation:

void main() {
    import std.stdio;
    foreach (ref i; 0 .. 10) {
        writeln(i);
        i++;
    }
}


Generally in my D code I write like this, as explained here:
http://blog.knatten.org/2011/11/11/disempower-every-variable/


void main() {
    import std.stdio;
    foreach (immutable i; 0 .. 10) {
        writeln(i);
        i++; // Not allowed.
    }
}


If your 'i' variable or your function arguments are const/immutable, you will have less surprises in your code (and a little less bugs). In D it's better to use const/immutable everywhere you can. Immutable by default is probably the right choice for a modern language. Unfortunately D has missed this opportunity.


I think this is one part of the larger problem of representing units.

Units of measure introduce a good amount of complexity and logic that is absent in the idea of giving strongly types to array indexes. You don't need to convert inches to centimetres, you don't need to perform a compile-time dimensional analysis for the correctness of expressions, you don't need to keep in mind the 0-based problems of converting Celsius to Fahrenheit, you don't need to solve the syntax problems of attaching "m / s" somehow to number literals, and so on. So they are two rather different problems.


though I'll have to think about whether it covers all
the cases you're interested in.

Another problem is visible in this code, I have a function bar() that must receive a different value for each item of the enum Foo. If I use an associative array, the compiler doesn't catch at compile-time the bug of the missing Foo.B case:


enum Foo : ubyte { A, B }
void bar(int[Foo] aa) {}
void main() {
    bar([Foo.A: 10]);
}


And often using an associative array for this purpose is a waste of memory and computation.

The Ada compiler is able to catch this bug at compile-time, using a simple fixed-size array (exactly as long as the number of items in the enum) with indexes strongly typed as Foo. So the array literal must contain all possible indexes only once:


import std.traits: EnumMembers;
enum Foo : ubyte { A, B }
void bar(int[@typed(Foo) EnumMembers!Foo.length] input) {}
void main() {
    bar([Foo.A: 10, Foo.B: 20]);
}


If you give a strong type to the array index you avoid that bug at compile time. (To be accepted as index type, an enumeration must behave nicely, all such tests can be done at compile-time).

Bye,
bearophile

Reply via email to