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]));
}