-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Record Enum Types #6739
Comments
I think there'd be a lot of potential with treating "class/struct enums" as ADTs. As a syntax I think it would be intuitive to users already familiar with C# enums. In fact I think I'd be quite happy if said "enums" would considered specifically in how they would benefit in combination with pattern matching and ADTs rather than just copypasta of Java's implementation. |
A minor quibble, neither of these are "complete" as I can make any sequence of 96 bits into a That said, I can do the same thing with an This sort of gets to the root of my problem with the |
@gafter Have you guys considered it for C# and the patent is really a roadblock? I'm certain other languages besides Java have OO enums. Pretty ironic if a patent with your name on it now prevents you from doing something similar in your current role. |
Actually it looks like Swift has gone this route for ADTs. enum Barcode {
case UPCA(Int, Int, Int, Int)
case QRCode(String)
}
let productBarcode = .QRCode("ABCDEFGHIJKLMNOP")
switch productBarcode {
case .UPCA(let numberSystem, let manufacturer, let product, let check):
print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
case .QRCode(let productCode):
print("QR code: \(productCode).")
} I am certainly not a lawyer but I would suspect that such an implementation would differ from the quoted patent enough. I assume that Apple did not pay to license that patent. |
@HaloFour I don't see how you read any part of that patent in Swift's language feature. Swift's enums do not have a closed set of values, they have a closed set of types. |
I'm not. My assumption is that the implementation is so different as to be unrelated and therefore not infringing. But I'm not a patent lawyer nor am I terribly versed in patent law so I don't want to make too many assumptions as to how wide that Java patent could be applied. I'll defer to your expertise in that matter.
It seems that Swift is trying to accomplish both, or at least make it syntactically simple enough to feel like values in the simpler cases. Either way I think it accomplishes closed-ADTs in a fairly intuitive syntax. |
@gafter So that is just concerning |
@alrz Yes, although Java's enums are actually reference types. |
@gafter And that's because Java doesn't have user-defined value types yet 😄 By the way, I found the Java implementation confusing, because, as you said, it's a "closed set of values", but at the same time, you can override methods for every enum member. Then it becomes a closed set of types (or more precisely, a closed set of instances of various subclasses of the enclosing type). This proposal clearly distinguishes these two concepts with |
@alrz In Java, the (static) types of the members are the type of the enum. Using |
@gafter I said it's "clear" because you can not possibly have a closed set of subtypes with |
@alrz Yes, I like the single keyword |
@gafter Not just public sealed abstract class Option<T> {
public sealed class Some(T Value) : Option<T>;
public sealed class None() : Option<T>;
} However, I do believe that But, about nesting issue, I've been thinking about this before. There are some options and pitfalls:
I don't know which direction you guys are going to take. |
@gafter I just had a thought. I wanted to open a new issue but I would rather post it here since it's related to the subject. Parts of this already proposed but I'm suggesting a unified syntax so one can get similar effect on various contexts. It is proposed to use public using class EmailAddress = String;
public using struct Identity = Int32; An optional type-parameter-list would be allowed in these declarations. I propose using enum class Option<T> {
Some(T Value),
None
} This has the same effect that var some = Some<T>(value);
var none = None<T>(); This should also work for public using abstract sealed class Foo<T> {
public sealed class Bar<U>() : Foo<T>;
} Then var bar = Bar<T;U>(); would be equivalent to var bar = Foo<T>.Bar<U>(); I think this would pretty much solve the problem with nesting, What do you think? |
While I agree it would "pretty much solve the problem with nesting" it feels like an awful lot of new syntax and mechanism to address the problem. I think we'd have to be very careful what things are brought into scope by the implicit |
@gafter I don't see an "implicit" We do carelessly bring all of the types into the scope with |
@alrz Not particularly related to these proposals but I always figured that |
@HaloFour Unfortunately, you can have a In any case, this isn't just about Some/None. It is about more general Algebraic Data Types, of which Some/None is just one example. |
Sure, I understand that I always thought that a Anyway that's all a tangent to the proposal of using |
@gafter there are two ways to solve it. One is to throw in the constructor. Another is to hide the constructors and use a factory method that returns a Yes, this will not work that well with pattern matching (unless you can use |
@orthoxerox Nullability practically breaks the completeness of ADTs. While any of your solutions would solve the problem with |
We are seriously considering switch (color)
{
case Blue: // shorthand for Color.Blue if the old name lookup rules would fail
// etc
} and Option<Foo> o = ...;
switch (o)
{
case Some(var foo): // shorthand for Option<Foo>.Some
// etc
} |
@gafter This is just great, however I don't know if it works for generics within generics (assuming that |
@alrz That it would, which would most likely cause match expressions to throw at runtime assuming that didn't have additional cases to force completeness like a wildcard pattern. |
Actually poking into some F#-generated IL and I find it amusing that the tag is not a field, it's a property, and it's calculated through type checks, e.g.: public int Tag {
get {
if (this is Foo) return Tags.Foo;
if (this is Bar) return Tags.Bar;
return Tags.Baz;
}
} And when matching against the ADT it doesn't appear to use this property anyway, it uses the IL opcode Not that it has anything to do with what C# may or may not do, I just find it amusing. |
@HaloFour Yeah, that was weird. It doesn't always use Tag for type checks (e.g. in helper methods), even |
@alrz I have seen a microbenchmark somewhere that showed that |
@orthoxerox But it's a single virtual call vs multiple isinst, if that wasn't the case we wouldn't need to bother about Tag at all. |
@alrz You'll have to ask someone like Don Syme to get the real answer. Could the JIT compiler optimize the sequence of |
It's interesting that F# also use the "singleton" instance for empty DU members. I don't see why it can't be used for |
Would like it to be the same syntax as normal enum I mean public enum Mode : int { A=1,B=2,C=3 }
public enum Color : Byte4 { Red=new Byte4(255,0,0,255) } |
I think I was propose idea of pure struct somewhere I mean struct that composed only native valuetype (byte/int/float/bool family) and/or a struct that also pure. These kind of struct should be able to do things like int could do. Such as make it const, use as base of enum (this proposal), or use in switchcase, etc,etc,etc. That would be useful |
Actually it seems that F# has some kind of threshold to generate tag field; when you have numerous DU members, then it generates something like this: public enum class Literal {
None,
Integer(int),
}
public class Literal
{
public static class Tags
{
public const int None = 0;
public const int Integer = 1;
}
public int Tag { get; }
private Literal(int tag)
{
this.Tag = tag;
}
public bool IsNone => this.Tag == Tags.None;
public bool IsInteger => this.Tag == Tags.Integer;
public static Literal None { get; } = new Literal(Tags.None);
public sealed class Integer : Literal
{
public int Item { get; }
public Integer(int item) : base(Tags.Integer)
{
this.Item = item;
}
}
} I didn't think about how this can support non-flat hierarchies though. |
@alrz I would like to differentiate between ADT and the typical closed set of constant values enum. Because the former denotes types while the latter denotes values/objects. I propose the following syntax for typical enum, reusing the tuple literal and current enum syntax- public enum Planet : (double Mass, double Radius)
{
Mercury = (3.303e+23, 2.4397e6),
Venus = (4.869e+24, 6.0518e6),
Earth = (5.976e+24, 6.37814e6),
Mars = (6.421e+23, 3.3972e6),
Jupiter = (1.9e+27, 7.1492e7),
Saturn = (5.688e+26, 6.0268e7),
Uranus = (8.686e+25, 2.5559e7),
Neptune = (1.024e+26, 2.4746e7),
Pluto = (1.27e+22, 1.137e6);
public const double G = 6.67300E-11;
public double SurfaceGravity => G * Mass / (Radius * Radius);
public double SurfaceWeight(double otherMass) =>
otherMass * SurfaceGravity;
} Beside tuples,enums will also be able to inherit classes and structs- public struct Color(int R, int G, int B);
public enum CommonColors : Color
{
Blue = new Color(0,0,255),
Green = new Color(0,255,0),
Red = new Color(255,0,0)
} And, within the class/struct definition, there can be public struct Color(int R, int G, int B)
{
public enum this
{
Blue = new Color(0,0,255),
Green = new Color(0,255,0),
Red = new Color(255,0,0)
}
}
Color color = Color.Red; //Ok now For ADT, I support your proposed syntax. |
Discussion should continue at dotnet/csharplang#485 |
I came onto this thread via reddit, but i actually wrote a package that achieves something similar. https://github.com/DylanSnel/EpicEnums feel free to check it out! |
Record Enum Types
Currently,
enum
types wouldn't be considered as complete patterns and they don't have any "record" syntax to be used in pattern matching. This proposal tries to fill this gap.This proposal won't affect regular
enum
types like #3704, rather, it suggests anenum
-like syntax for declaring flat hierarchies of ADTs (with both value and reference types).Enum structs
Enum structs would be more like Java enum types, for example
would translate to
Another example from Java docs:
This struct must not be instantiable, because of the completeness of the pattern.
Enum classes
Enum classes are useful for declaring flat hierarchy of ADTs (similar to F# discriminated unions). For example. the following
could be written as
With enum classes,
abstract sealed
would be considered as an advanced feature where you can declare more complicated ADTs.Remarks
Option<T>
in this example.abstract
and cannot extend any other types.The text was updated successfully, but these errors were encountered: