Optionally strongly typed array indexes
bearophile via Digitalmars-d
digitalmars-d at puremagic.com
Thu Jun 5 02:22:58 PDT 2014
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
More information about the Digitalmars-d
mailing list