Optionally strongly typed array indexes
Mason McGill via Digitalmars-d
digitalmars-d at puremagic.com
Thu Jun 5 15:01:55 PDT 2014
On Thursday, 5 June 2014 at 09:23:00 UTC, bearophile wrote:
> Sorry for my answers coming so slowly.
Hah! I'm on these forums (selfishly) trying to understand/improve
D for my own use at work, while you seem to be
helping/teaching/contributing for the sake of it. I'm continually
surprised by how pleasant and professional this community is;
nothing at all to be sorry for!
> 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.
That is weird. It goes counter to the idea of `i` "coming from"
the range. Though, I'd say someone writing that code is asking
for strange behavior, so it's not much of an issue.
> Generally in my D code I write like this, as explained here:
> http://blog.knatten.org/2011/11/11/disempower-every-variable/
I agree, with the caveat of trying to not add so much "noise"
that it becomes unreadable.
> Immutable by default is probably the right choice for a modern
> language.
Yes, or at least making operations with side effects look
different from operations without.
>> 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,
True.
> You don't need to perform a compile-time dimensional analysis
> for the correctness of expressions.
I'd argue you do, at least given my understanding of the goals of
strongly-typed array indices. For example, consider strided
indexing:
const dogs = ["Rover", "Fido", "Rex"].of!"dog";
const cats = ["Fluffy", "Mr. Whiskers"].of!"cat";
// This should work.
const i1 = index!"dog"(1);
dogs[i1];
// This should not work.
const i2 = index!"cat"(1);
dogs[i2];
// This should work.
const i3 = 2 * i1;
dogs[i3];
// This should not work.
const i4 = i2 * i1;
dogs[i4];
So, the library needs to do some form of dimensional analysis to
allow typed indices to be multiplied by unit-less scalars, but
not scalars with units.
> You don't need to keep in mind the 0-based problems of
> converting Celsius to Fahrenheit.
True. This is why I said the problem of strongly typed array
indexes is *part* of the problem of units, not equivalent to it.
> You don't need to solve the syntax problems of attaching "m /
> s" somehow to number literals.
Also true, though as a side note, I think a library solution for
this could be quite nice:
enum newton = 1.as!"kg*m/s^2"; // One possibility.
enum newton = 1*kg*m/square(s); // Another.
>> 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).
That's quite clever, though couldn't it also be done with a
library?
enum Foo : ubyte { A, B }
enum nFoo = EnumMembers!Foo.length;
alias AllFoo = WithUnits!(int[nFoo], Foo);
void bar(AllFoo) {}
void main() {
bar(AllFoo([/*A*/ 10, /*B*/ 20]));
}
More information about the Digitalmars-d
mailing list