-
Notifications
You must be signed in to change notification settings - Fork 23
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
Overloadable enum names #373
Comments
I run into this issue all the time:
I run into this issue all the time having to prefix everything and not having it match with the APIs I am communicating with. And the annoying thig is that nim could perfectly tell the enums apart. There is almost never any ambiguity, if its a Thank you for putting this RFC together. |
I don't see how prefixes are required for enums, it's an archaic convention in Nim's stdlib but apart from that, nobody asks you to use prefixes. I personally moved to non-pure UpperCased enum field names and it works well. Ymmv of course. |
It appears that prefixes are required because of enum collision see here: type
HAlignMode* = enum
Default
Left
Center
Right
VAlignMode* = enum
Default
Top
Middle
Bottom
https://play.nim-lang.org/#ix=3lyx I don't want to use |
It's not that prefix is required but they're the more ergonomic method of writting enums in my view. Reducing the amount you have to specify the type. CssAlign {.pure.} = enum
left, right, center
Alignment {.pure.} = enum
left, right, center
Justify {.pure.} = enum
left, right, center Presently we need to do let yourVal = CssAlign.left
case yourVal:
of CssAlign.left: ...
of CssAlign.right: ...
of CssAlign.center: ... In this example we had to declare the enum's type 4 sperate times, so if we use case 10u8
of 100u8..200u8: discard
of 254u8..255u8: discard
else: discard instead case 10u8
of 100..200: discard
of 254..255: discard
else: discard |
It's not just here which is simply a better experience for folks, which is a good thing. The technique of propagating expected type information can be used for additional improvements in inference/disambiguation, this is a very interesting direction. It also seems to be a good vector for bringing in new folks to the compiler. As long as there is a principle(s) for the direction in which information the type information propagates (which this doesn't violate existing rules), then this and other such improvements can move forward. |
Here is how it can work:
|
Another example from my pretty Current way, Hungarian notation very C/win32 like. case node.kind:
of nkNumber, nkBool:
printStr(fgBlue, node.value)
of nkRepeat, nkNil, nkPointer:
printStr(fgRed, node.value)
of nkProc:
printStr(fgMagenta, node.value)
of nkString:
printStr(fgGreen, node.value.escapeString())
of nkChar:
printStr(fgGreen, "'" & node.value.escapeString()[1..^2] & "'")
of nkSeq, nkArray: I want: fees very clean and Nim like. case node.kind:
of Number, Bool:
printStr(Blue, node.value)
of Repeat, NilPointer, Pointer:
printStr(Red, node.value)
of Procedure:
printStr(Magenta, node.value)
of String:
printStr(Green, node.value.escapeString())
of Char:
printStr(Green, "'" & node.value.escapeString()[1..^2] & "'")
of Seq, Array: I don't want, feels very bureaucratic/java like, tons of repeating names hiding the code: case node.kind:
of PrintNodeKind.Number, PrintNodeKind.Bool:
printStr(ForegroundColor.Blue, node.value)
of PrintNodeKind.Repeat, PrintNodeKind.Nil, PrintNodeKind.Pointer:
printStr(ForegroundColor.Red, node.value)
of PrintNodeKind.Proc:
printStr(ForegroundColor.Magenta, node.value)
of PrintNodeKind.String:
printStr(ForegroundColor.Green, node.value.escapeString())
of PrintNodeKind.Char:
printStr(ForegroundColor.Green, "'" & node.value.escapeString()[1..^2] & "'")
of PrintNodeKind.Seq, PrintNodeKind.Array: |
b3liever proposed this code is fine : const
pagesToCstring: array[PageMode, cstring] = mapLiterals([
PageMode.none: "home",
PageMode.home: "home",
PageMode.about: "about-us",
PageMode.media: "media-kit",
PageMode.help: "help",
PageMode.cookies: "cookies",
PageMode.privacy: "privacy-policy",
PageMode.terms: "user-terms",
PageMode.account: "account",
PageMode.cart: "cart",
PageMode.checkout: "checkout",
PageMode.error404: "error404",
], cstring) (From https://forum.nim-lang.org/t/7983#50950) I think this code is better: const
pagesToCstring: array[PageMode, cstring] = mapLiterals([
None: "home",
Home: "home",
About: "about-us",
Media: "media-kit",
Help: "help",
Cookies: "cookies",
Privacy: "privacy-policy",
Terms: "user-terms",
Account: "account",
Cart: "cart",
Checkout: "checkout",
Error404: "error404",
], cstring) In Nim we rely on pretty type inference we should allow it to be used in more places. |
+1 This improvement is very much needed. Having to type P.S. I think this code is even better, it's TypeScript using LiteralType, and the correct enum names usage will be validated at compile time, but I'm not willing to push Nim enum that far, just the ability to resolve enum names conflict would be good enough :) type Pages = "home" | "about-us" | "media-kit" | "help" |
"cookies" | "privacy-policy" | "user-terms" | "account" | "cart" |
"checkout" | "error404" |
Some approaches from other languages: |
Correct as I cannot think of a more natural solution for Nim. |
Any of the approaches would allow @treeform to write the cleaner version he prefers. I agree that overloading is the natural approach for Nim, as the Rust approach would require some new local scoping mechanisms. |
Another place this could be useful is in expressions, take the following into consideration. proc bar = discard
proc foo: proc() =
result = proc() =
discard
proc doSomething: proc() =
case true:
of true: bar
of false: foo() Presently this errors at |
@beef331 This is true but completely unrelated to the enum situation. This RFC should be split up into different parts. This RFC should be called "Overloadable enum names" |
I think this is same thing: #8 |
We run into exact same in again Pixie: We want to mimic how JS canvas API works, but it has "round" for both |
I finally found more or less nice and clean way to use enums, with json-like helper. It solves 5 issues of Nim.
This example has larger scope than this issue focussed on enums, but I would like to highlight the wole use case. Why these features needed. plot "/test_table.json", rows, jo {
columns: [
{ id: "name", type: "string" }, # <= "string" going to be casted to enum
# and reserved word "string" used for enum value
{ id: "age", type: "number" } # <= reserved word "type" used for key name
]
}
type PlotDataType* {.pure.} = enum
string_e = "string",
number_e = "number",
boolean_e = "boolean",
unknown_e = "unknown"
type PlotTableColumn* = object
id*: string
`type`*: PlotDataType
proc plot*[Row](path: string, rows: seq[Row], options: TableOptions): void =
discard Code for jo helper P.S. I didn't invented this example just to point to those Nim issue. This is real code that I'm converting from some JavaScript table/plotting library. |
This has been implemented. |
I tried the example in #8, it works type
Enum1 = enum
A, B, C
Enum2 = enum
A, Z
proc f(e: Enum1): int = ord(e)
proc g(e: Enum2): int = ord(e)
proc h(e: Enum1): int = ord(e)
proc h(e: Enum2): int = ord(e)
let fA = f(A) # Type of A is well defined
let gA = g(A) # Same as above
let hA1 = h(Enum1.A) # A requires disambiguation
let hA2 = h(Enum2.A) # Similarly
let x = ord(Enum1.A) # Also But won't work with case of {.experimental: "overloadableEnums".}
type
A {.pure.} = enum
left, right, up, down
B {.pure.} = enum
left, right, up, down
let test = A.left
case test:
of left: discard
of up: discard
else: discard
nim version
|
@derekdai |
It seems like enum overload works for parameter and assignment but not for initialisation? play {.experimental: "overloadableEnums".}
type
E1 = enum a, b
E2 = enum a, b
proc test(e: E1) = echo e
test(a) # Works, parameter
var v1 = E1.a
v1 = a # Works, assignment
let v2: E1 = a # Error? |
@al6x top-down type inference solve this elegantly. |
Following my dud of a PR nim-lang/Nim#17897, making a RFC as suggested.
Given that the information is there, it seems sensible to pass the type information from the pertinent parts of statements to make the user not have to explictly specify the type so much. It is especially useful for enums, where you have many duplicate enum names and dont want to add the prefix notation (Treeform can attest it's not nice 😄 ).
The following is atleast some subset of what this type inference could enable.
The text was updated successfully, but these errors were encountered: