I think enum classes are quite limited. From documentation:
Enums are a special type of class that define a discrete range of possible values.... Enums are declared using the enum keyword and must declare their range of values before any other slot declarations...Enums can declare their own constructor to initialize additional fields....all fields must be initialized in the constructor. Enum constructors must be private.
enum Color { red, blue, green }
Other languages allows more powerful uses of enum. I wonder if it's posible/convenient to incorporate part/all that functionality in Fantom
I'll use Haxe language (www.haxe.org) as an example. From Haxe documentation:
Enums are different to classes and are declared with a finite number of constructors
enum Color {
red;
green;
blue;
}
but also
enum Color2 {
red;
green;
blue;
grey( v : Int );
rgb( r : Int, g : Int, b : Int );
}
Haxe enums have limited number of constructors, but unlimited legal values
In addition, switch sentence has a special behavior when used on an enum. If there is no default case then it will check that all an enum's constructors are used within the switch, and if not the compiler will generate a warning (when you add new constructors to your enum, compiler will alert you to areas in your program where the new constructor should be handled)
If enum constructor have parameters, they must be listed as variable names in a switch case. This way all the variables will be locally accessible in the case expression and correspond to the type of the enum constructor parameter. For example, using the Color2 enum:
class Colors {
static function toInt( c : Color2 ) : Int {
return switch( c ) {
case red: 0xFF0000;
case green: 0x00FF00;
case blue: 0x0000FF;
case grey(v): (v << 16) | (v << 8) | v;
case rgb(r,g,b): (r << 16) | (g << 8) | b;
}
}
}
Using switch is the only possible way to access the enum constructors parameters.
NOTE.- Haxe also allows enums as recursive types, and generics with enums (type parametrization), but this is another story
Accesing constructor parameters as variable names in a switch case is not "full pattern-matching" (it's not Scala) but the resulting code is simple and readable.
tacticsWed 3 Feb 2010
Fantom's enum isn't really limited. It's actually more powerful than many other languages which have them.
What you're describing is an algebraic data type, like the ones used in ML and Haskell. Algebraic data types are even more general than enums, for sure.
Generalizations are cool, but they always come with a cost. That cost is often complexity in the interface (from the programmer's point of view), implementation (which affects tool developers), add more for beginners to learn, and requires more effort to break down when reading code. These subtle costs, in my opinion, are greatly underestimated by most developers. Brian and Andy are very conscious of them.
The question of generalizing enums (or any other language feature) isn't about how powerful we can make it, but rather, how can we hit the sweet spot where power is maximized and complexity is minimized.
Making a change like this requires making nontrivial changes to the switch statement. It also places a new importance on the switch statement, which is now required to deconstruct these kinds of objects. It will also open the flood gates for users who have ML/Haskell experience pushing for more advanced typing features.
For something like this, you're better off making two classes: an enum for the color type and a class to hold optional color data. You can store the color data as an Obj? and then assign to it a Float or a Float[] for grey and rgb data. Automatic upcasting and runtime typing take care of the rest.
qualidafialWed 3 Feb 2010
The only thing I've missed in Fantom's enums is the ability to make each instance an anonymous subclass like in java:
public abstract enum SizeStrategy {
MINIMUM() { public Point getSize(Widget w) { return w.getMinimumSize(); } },
MAXIMUM() { public Point getSize(Widget w) { return w.getMaximumSize(); } },
PREFERRED() { public Point getSize(Widget w) { return w.getPreferredSize(); } };
public abstract Point getSize(Widget w);
}
Although you can sort of accomplish the same thing in Fantom by passing Func instances to the enum constructor:
I'd have to agree with tactics, Fantom's enum's are more powerful than those in C# and Java. For instance, the fact that you can rely on forward-referencing for building up recursive relationships is super nice for state modeling. I for one appreciate that Fan is not Scala.
brianWed 3 Feb 2010
If we ever did pattern matching, I don't think we would base it on enum. Although eventually I would like to make switch more powerful (not sure it is full pattern matching).
Having the compiler detect that not all enums were used in a switch is an interesting concept. Although I'd worry it would be annoying if you didn't really want to use them all. You can always default to throw a runtime exception (assuming you have unit tests). But I'd be interested if others thought that would be useful check that didn't get in the way.
I also do want to add a bitmask class (like C# does with enums), but a proper OO class. I think something like that is more needed now we dropped bitwise operators.
casperbangWed 3 Feb 2010
Having the compiler detect that not all enums were used in a switch is an interesting concept. Although I'd worry it would be annoying if you didn't really want to use them all. You can always default to throw a runtime exception (assuming you have unit tests). But I'd be interested if others thought that would be useful check that didn't get in the way.
I could see this as a handy way to tighten up idioms (avoid secondary catch-all) much in the same way as Fantom does away with fall-through cases. If not all cases are covered explicitly or implicitly via a default case, the code won't compile!
However, what about versioning? What would happen if the enum gets another value added without the switch code being recompiled? This is a common problem i.e. in JPA.
brianWed 3 Feb 2010
However, what about versioning? What would happen if the enum gets another value added without the switch code being recompiled?
Good point, that is where you'd want a runtime exception. Although the compiler could generate that:
switch (orient)
{
case Orientation.horizontal: ...
case Orientation.vertical: ...
}
If no default was specified, then it would be a compiler error if not all enum values were used and we would generate an implicit default that threw an Err.
casperbangWed 3 Feb 2010
If no default was specified, then it would be a compiler error if not all enum values were used and we would generate an implicit default that threw an Err.
Clever yet straight forward, a nice way to avoid surprises as an enum evolves! +1
jodastephenWed 3 Feb 2010
I would also like to see switch on enum force use of all values or default as above. Its a useful error avoidance mechanism.
jsendra Wed 3 Feb 2010
I think enum classes are quite limited. From documentation:
Other languages allows more powerful uses of enum. I wonder if it's posible/convenient to incorporate part/all that functionality in Fantom
I'll use Haxe language (www.haxe.org) as an example. From Haxe documentation:
but also
NOTE.- Haxe also allows enums as recursive types, and generics with enums (type parametrization), but this is another story
Accesing constructor parameters as variable names in a switch case is not "full pattern-matching" (it's not Scala) but the resulting code is simple and readable.
tactics Wed 3 Feb 2010
Fantom's enum isn't really limited. It's actually more powerful than many other languages which have them.
What you're describing is an algebraic data type, like the ones used in ML and Haskell. Algebraic data types are even more general than enums, for sure.
Generalizations are cool, but they always come with a cost. That cost is often complexity in the interface (from the programmer's point of view), implementation (which affects tool developers), add more for beginners to learn, and requires more effort to break down when reading code. These subtle costs, in my opinion, are greatly underestimated by most developers. Brian and Andy are very conscious of them.
The question of generalizing enums (or any other language feature) isn't about how powerful we can make it, but rather, how can we hit the sweet spot where power is maximized and complexity is minimized.
Making a change like this requires making nontrivial changes to the switch statement. It also places a new importance on the switch statement, which is now required to deconstruct these kinds of objects. It will also open the flood gates for users who have ML/Haskell experience pushing for more advanced typing features.
For something like this, you're better off making two classes: an enum for the color type and a class to hold optional color data. You can store the color data as an
Obj?
and then assign to it aFloat
or aFloat[]
for grey and rgb data. Automatic upcasting and runtime typing take care of the rest.qualidafial Wed 3 Feb 2010
The only thing I've missed in Fantom's enums is the ability to make each instance an anonymous subclass like in java:
Although you can sort of accomplish the same thing in Fantom by passing Func instances to the enum constructor:
casperbang Wed 3 Feb 2010
I'd have to agree with tactics, Fantom's enum's are more powerful than those in C# and Java. For instance, the fact that you can rely on forward-referencing for building up recursive relationships is super nice for state modeling. I for one appreciate that Fan is not Scala.
brian Wed 3 Feb 2010
If we ever did pattern matching, I don't think we would base it on enum. Although eventually I would like to make switch more powerful (not sure it is full pattern matching).
Having the compiler detect that not all enums were used in a switch is an interesting concept. Although I'd worry it would be annoying if you didn't really want to use them all. You can always default to throw a runtime exception (assuming you have unit tests). But I'd be interested if others thought that would be useful check that didn't get in the way.
I also do want to add a bitmask class (like C# does with enums), but a proper OO class. I think something like that is more needed now we dropped bitwise operators.
casperbang Wed 3 Feb 2010
I could see this as a handy way to tighten up idioms (avoid secondary catch-all) much in the same way as Fantom does away with fall-through cases. If not all cases are covered explicitly or implicitly via a default case, the code won't compile!
However, what about versioning? What would happen if the enum gets another value added without the switch code being recompiled? This is a common problem i.e. in JPA.
brian Wed 3 Feb 2010
Good point, that is where you'd want a runtime exception. Although the compiler could generate that:
If no
default
was specified, then it would be a compiler error if not all enum values were used and we would generate an implicitdefault
that threw an Err.casperbang Wed 3 Feb 2010
Clever yet straight forward, a nice way to avoid surprises as an enum evolves! +1
jodastephen Wed 3 Feb 2010
I would also like to see switch on enum force use of all values or
default
as above. Its a useful error avoidance mechanism.