On Monday, 30 November 2015 at 07:48:37 UTC, Andrew LaChance
wrote:
Hello,
D has intrigued me for a while, and I thought I would finally
read up on it! I've been reading "Programming in D" by Ali
Çehreli and I've been thinking about how I can use the language
in a side project I'm working on, porting it from java to D.
One of the uncommonly-used features of java that I like is how
enums can be full classes (though I don't like that there's no
option to use enums as e.g. regular ints). This allows several
benefits, such as the ability to use them in switch statements
like regular enums, the full set of objects is known at compile
time, all objects are immutable, it's impossible to
accidentally or purposefully create new objects of that type,
etc...
For example (in java), if I wanted to have an enum that
describes all the white keys on a piano keyboard and have
members that describe the number of half-steps to the next
white key and to the previous white key, I can define an enum
(the "id" or enum value is implicitly defined so it doesn't
have to be explicitly written in the definition):
enum WhiteKey
{
A(2,2),
B(2,1),
C(1,2),
D(2,2),
E(2,1),
F(1,2),
G(2,2);
private final int halfStepsToNext;
private final int halfStepsToPrevious;
WhiteKey(int halfStepsPrevious, int halfStepsNext)
{
this.halfStepsToPrevious = halfStepsPrevious;
this.halfStepsToNext = halfStepsNext;
}
}
From what I've read and seen, in D all enums have forced to
integral types. Is it possible to do the above in D and I have
just missed it? I can think of a few ways around it (such as
statically create and define a bunch of WhiteKey structs, ...),
but none are as clean as the above. If this isn't something
supported, is it on a roadmap of wanted features?
Thanks! I'm looking forward to really getting to know the
language.
Yes and no. You can use arbitrary types for enums in D but a lot
of the time you shouldn't when it involves types that are not
Plain Old Data. A naive translation would be like this:
class WhiteKey
{
private immutable int halfStepsToNext;
private immutable int halfStepsToPrevious;
enum
{
A = new WhiteKey(2, 2),
B = new WhiteKey(2, 1),
C = new WhiteKey(1, 2),
D = new WhiteKey(2, 2),
E = new WhiteKey(2, 1),
F = new WhiteKey(1, 2),
G = new WhiteKey(2, 2),
}
private this(int halfStepsToPrevious, int halfStepsToNext)
{
this.halfStepsToPrevious = halfStepsToPrevious;
this.halfStepsToNext = halfStepsToNext;
}
}
However, you do NOT want to do this, as everywhere you use
WhiteKey's members, a new object will be created. For example:
auto f = WhiteKey.A;
auto n = WhiteKey.A;
import std.stdio;
writeln(&f, " ", &n);
This will two different addresses, because a new object is being
created each time. It's basically taking the expression `new
Key(2, 2)` and copy-pasting it wherever you use WhiteKey.A.
Java's enums are basically syntax sugar for this:
class WhiteKey
{
private immutable int halfStepsToNext;
private immutable int halfStepsToPrevious;
public static WhiteKey A = new WhiteKey(2, 2);
public static WhiteKey B = new WhiteKey(2, 1);
public static WhiteKey C = new WhiteKey(1, 2);
public static WhiteKey D = new WhiteKey(2, 2);
public static WhiteKey E = new WhiteKey(2, 1);
public static WhiteKey F = new WhiteKey(1, 2);
public static WhiteKey G = new WhiteKey(2, 2);
private this(int halfStepsToPrevious, int halfStepsToNext)
{
this.halfStepsToPrevious = halfStepsToPrevious;
this.halfStepsToNext = halfStepsToNext;
}
}
This doesn't quite work in D; you'd have to make each WhiteKey
const (which is probably not a bad idea anyway if you're using it
like an enum). However, it's better to just do this with plain
old value-type structs. It's exactly the same as my previous code
defining a WhiteKey class with an embedded enum, but using a
struct instead of a class.