Not being familiar with ocaml or haxe, and not being clever enough to understand the other explanations, I went and looked up the haxe enum documentation - the 'Enum Type Parameters' bit at the bottom seems to be the relevant part.
My understanding based on that is as follows:
A 'normal' enum is basically a value which is restricted to the things that you have defined in your enum definition. C# Example:
enum Color{ Red, Green, Yellow, Blue };
Color c = Color.Red;
c
can either be Red
, Green
, Yellow
, or Blue
, but nothing else.
In haxe, you can add complex types to enums, Contrived example from their page:
enum Cell<T>{
empty;
cons( item : T, next : Cell<T> )
}
Cell<int> c = <I don't know>;
What this appears to mean is that c
is restricted to either being the literal value empty
(like our old fashioned C# enums), or it can also be a complex type cons(item, next)
, where item
is a T
and next
is a Cell<T>
.
Not having ever used this it looks like it is probably generating some anonymous types (like how the C# compiler does when you do new { Name='Joe'}
.
Whenever you 'access' the enum value, you have to declare item
and next
when you do so, and it looks like they get bound to temporary local variables.
Haxe example - You can see 'next' being used as a temporary local variable to pull data out of the anonymous cons structure:
switch( c ) {
case empty : 0;
case cons(item,next): 1 + cell_length(next);
}
To be honest, this blew my mind when I 'clicked' onto what it seemed to be doing. It seems incredibly powerful, and I can see why you'd be looking for a similar feature in C#.
C# enums are pretty much the same as C/++ enums from which they were originally copied. It's basically a nice way of saying #define Red 1
so the compiler can do comparisons and storage with integers instead of strings when you are passing Color
objects around.
My stab at doing this in C# would be to use generics and interfaces. Something like this:
public interface ICell<T> {
T Item{ get; set; }
ICell<T>{ get; set; }
}
class Cons<T> : ICell<T> {
public T Item{ get; set; } /* C#3 auto-backed property */
public Cell<T> Next{ get; set; }
}
class EmptyCell<T> : ICell<T>{
public T Item{ get{ return default(T); set{ /* do nothing */ }; }
public ICell<T> Next{ get{ return null }; set{ /* do nothing */; }
}
Then you could have a List<ICell<T>>
which would contain items and next cell, and you could insert EmptyCell
at the end (or just have the Next
reference explicitly set to null).
The advantages would be that because EmptyCell
contains no member variables, it wouldn't require any storage space (like the empty
in haxe), whereas a Cons
cell would.
The compiler may also inline / optimize out the methods in EmptyCell
as they do nothing, so there may be a speed increase over just having a Cons
with it's member data set to null.
I don't really know. I'd welcome any other possible solutions as I'm not particularly proud of my one :-)