nim-lang / Nim

Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula. Its design focuses on efficiency, expressiveness, and elegance (in that order of priority).
https://nim-lang.org
Other
16.59k stars 1.47k forks source link

Pure enums allow using the same name but allow nonqualification with quirky behaviour #8066

Closed metagn closed 6 years ago

metagn commented 6 years ago
type
  X {.pure.} = enum
    red

  Y {.pure.} = enum
    red

echo red # red, with unused warning for Y
let newRed = red # this works too and can be operated on

when false: # undeclared identifier: 'red' for both,
  echo red.ord
  from typetraits import `$`
  echo red.type
apahl commented 6 years ago

I am not sure if this is about Language Design. To me this looks like a bug or a regression, since code from the manual does not work as described anymore:

type
  MyEnum {.pure.} = enum
    valueA, valueB, valueC, valueD

echo valueA

The manual states that this code should give an error because of the pure pragma, but it compiles and runs fine.

KR Axel

haltcase commented 6 years ago

I agree this should be considered a bug, it defeats the whole purpose of using pure on an enum.

andreaferretti commented 6 years ago

There was a discussion not so long ago where @Araq argued that there is no issue in allowing non-qualified access to pure enums, as long as no ambiguity arises.

Hence the condition on pure enums was relaxed a bit. The problem here is that there is an ambiguity.

In my opinion, allowing only qualified access to pure enums is a simple and clear rule to explain and to follow, and I do not see much value in allowing non qualified access.

Araq commented 6 years ago

Yes, you don't see much value. I've actually tried to use the old pure enums though and they are terrible. {Foo.valA, Foo.valB} violates DRY and so you need even more complex rules like "In a set construction, the prefix can be avoided. Oh and in case statements too. Oh and in array constructions..."

Araq commented 6 years ago

And then these rules fail to be applied in DSLs...

andreaferretti commented 6 years ago

@Araq I actually don't have any problems in relaxing the conditions for pure enums, the current issue is just a bug that needs to be ironed out. I was just trying to reconstruct why things changed.

Even if I don't see much value in this particular circumstance, I am mostly agnostic about whatver choice gest implemented for pure enums

haltcase commented 6 years ago

I'd also be alright with it after hearing the rationale, provided this bug gets fixed and the compiler gives useful error messages similar to other ambiguities.

mratsim commented 6 years ago

Relevant discussion in 2017 from @dom96, @PMunch, @Araq and me https://irclogs.nim-lang.org/22-10-2017.html#13:54:23

Time Person Text
13:54:23 dom96 I don't get it. You don't have to prefix enum names even when {.pure.} is specified?
14:03:38 Araq dom96, pure enum field names get their own scope that is queried if the name was not found in any other scope
14:04:09 Araq so as long as nothing else clashes with it you can use these names without the Enum. prefix
14:04:35 dom96 So now the only difference is that I cannot use a prefix for non-pure enums
14:04:56 Araq er, sure you can
14:04:56 dom96 This behaviour should be implemented for non-pure enums and pure enums should have been left alone
14:05:19 dom96 so what's the difference between pure/non-pure enums now?
14:05:21 Araq well the point is to remove the whole distinction between the two enum types
14:05:57 dom96 er, so you've basically removed {.pure.}?
14:07:09 FromGitter \<mratsim> So this should be updated: https://nim-by-example.github.io/types/enums/
14:07:27 PMunch Pure was nice though..
14:07:44 dom96 It is nice.
14:07:48 Araq I unified the two concepts, not sure why you don't understand it
14:07:57 dom96 Which is why I'm trying to understand what the purpose of Araq's changes is
14:08:09 FromGitter \<mratsim> It should be named "qualified" instead of pure though.
14:08:09 Araq "pure was nice though", omg you can still prefix them all you want
14:08:23 PMunch But you can't force people to..
14:08:27 dom96 indeed
14:08:32 PMunch I thought that was the entire point of pure
14:08:39 dom96 precisely
14:08:43 Araq the point was to prevent clashes
14:08:50 PMunch To say: this symbol doesn't make any sense without the enum name
14:09:10 dom96 But you've just said that you can prefix non-pure enums too
14:09:21 dom96 so if there are clashes you can prevent them
14:09:31 PMunch And what happens if you have "OneEnum.test" and "AnotherEnum.test", both marked as pure. What will test refer to?
14:09:38 FromGitter \<mratsim> you will still get "ambiguous call" though? I guess itÔøΩs the same discussion as {.this:this.} pragma
14:09:56 Araq it never was about enforcing rules, it was about preventing clashes
14:10:14 Araq nobody wants these rules anyway:
14:10:17 Araq case e
14:10:24 Araq of SomeEnum.valueA:
14:10:28 Araq of SomeEnum.valueB:
14:10:38 dom96 I do
14:10:44 Araq of SomeEnum.valueC: echo "just kill me already"
14:10:57 dom96 Sometimes it makes sense to have a short enum value name
14:11:04 dom96 and then a qualifying prefix that is long
14:11:19 dom96 In that case I want to force the user of my library to prefix the enum
14:11:46 Araq yeah, that is exactly what Nim is NOT about
14:11:55 Araq your users are adults.
14:13:22 Araq if foo in {SomeEnum.short, shortToo, shorter}
14:13:38 Araq # ^ context is everything
14:16:27 PMunch Hmm, I still feel this should be handled somehow else..
14:16:51 dom96 okay, then we should get the ability to enforce this when importing enums
14:17:36 dom96 btw my book mentions the pure pragma
14:18:19 PMunch TBH, that shouldn't be a factor in the language design
14:18:30 PMunch Of course it's not good, but such is life..
14:19:22 dom96 True.
14:20:01 Araq people argued that every enum should be pure and then we should have special rules in 'case' and set literals to be able to avoid the prefix
14:21:06 PMunch That makes more sense..
14:21:13 Araq but this doesn't work well, what about [value: "a", valueB: "b"] array constructions
14:22:51 PMunch "SomeEnum.[value: val1, valueB: val2]" for "type SomeEnum = enum val1, val2"
14:23:05 PMunch Same for set
14:23:49 PMunch Case is the only special thing
14:24:37 Araq that's not true at all
14:25:41 Araq in Karax for example the problem comes up all the time too, we want a CSS "enum" without the prefixes that yet doesn't clash
14:26:11 Araq registerEvent(onclick, proc ...)
14:26:28 Araq # ok, onclick is nothing unexpected here
14:26:55 Araq registerEvent(Event.onclick, proc ...) # some other onclick clashed so I need to write it out
14:27:23 dom96 It's hard to tell that onclick is an enum type
14:27:33 dom96 it could be a procedure
14:27:41 Araq it's superior to your "I want to enforce it everywhere until I figured out that's a horrible idea"
14:27:43 dom96 forcing the prefix makes it clearer
14:27:49 dom96 It makes the code far more readable
14:27:54 PMunch Agreed
14:28:03 Araq no, I disagree and Nim doesn't work this way
14:28:20 miran prefix sometimes makes things clearer
14:28:33 Araq everything else in the language doesn't work this way either anyway
14:28:41 dom96 But sadly this indeed isn't how Nim was designed
14:29:37 Araq Event.onclick # you don't know anything about enums here, could also be ModuleName.onclick
14:29:38 PMunch Fair enough

I still think if the pragma is here to stay, {.pure.} should be renamed {.qualified.}, and {.qualified.} should always require the enum prefix. {.pure.} is not really descriptive of what the pragma does. (And maybe we should have import qualified Foo instead of from Foo import nil)

But, I think this is unnecessary policing by the compiler, assuming this is an internal enum, you're free to use your preferred style (always qualifying, or qualifying when necessary), if it's a public module, don't force your style on other projects.

All in all I think this should be handled in a Nim linter, with options chosen by the library authors, not in the compiler.

cooldome commented 6 years ago

Just an opinion, I like that you don't to prefix everything with MyEnum. it saves a lot of strokes. I think just error message needs to be improved for example above. Nim should say that red is ambiguous.

metagn commented 6 years ago

I think the focus was mishandled in this issue, it's also a matter of not having to deal with hungarian notation or the full name of the enum in the enum member like SDL_SCANCODE_UNKNOWN instead of Scancode.Unknown. To better express my point:

type StreetLight {.pure.} = enum
  red, yellow, green

let foo = StreetLight.red

instead of

type StreetLight = enum
  stlRed, stlYellow, stlGreen

let foo = stlRed