nim-lang / RFCs

A repository for your Nim proposals.
137 stars 23 forks source link

Only allow `pure`/specifically annotated enums to be overloaded #468

Closed metagn closed 2 years ago

metagn commented 2 years ago

Abstract

Require an annotation/pragma (preferably a repurposed/renamed {.pure.}) on enum types to be able to use their fields as overloads.

Motivation

The following code works without, but breaks with --experimental:overloadableEnums (https://github.com/nim-lang/Nim/issues/20077):

type Foo1 = enum abc
block:
  type Foo2 = enum abc
  doAssert abc is Foo2

A more important version of this problem is with imports. There are multiple tests for this, both without and with imports.

However, this is mostly intended behavior of overloadableEnums. Consider the following:

# a.nim
type A* = enum
  Left, Right

# b.nim
type B* = enum
  Left, Down

# c.nim
import a, b

type C* = enum
  Left, South

let what = Left

An ambiguity error is the most sensible outcome here; with --experimental:overloadableEnums there is an ambiguity error, without it, what has type C. So the behavior of overloadableEnums here is preferred.

The difference between the example code and the test cases is that in the example case, the enums would normally have the {.pure.} annotation, while other enums may not necessarily need fancy behavior like this. So maybe the best solution is to restrict this overloading behavior to pure enums, while also removing any remaining need for qualification of pure enums.

Description

pure as is is generally regarded to be useless and deserving deprecation because of it not working as documented, so giving it new meaning that is compatible with its common/intended use is acceptable. If a new name for this is used instead of pure, then pure can be deprecated and alias to the new name.

This will make the transition to --experimental:overloadableEnums easier for normal projects, as they can gradually enable certain enums to work this way. Projects already using --experimental:overloadableEnums will have to annotate their enums with pure. We could make pure the default somehow but this doesn't fit the narrative of them being "fancy" enums.

Code Examples

# a.nim
type A* {.pure.} = enum
  Left, Right
# b.nim
import a

type B* {.pure.} = enum
  Left, Down

when isMainModule:
  assert not compiles((let left = Left; left))
  assert compiles((let left: A = Left; left))
  assert compiles((let left: B = Left; left))
# c.nim
import a, b

type C* = enum
  Left, South

when isMainModule:
  assert Left is C
# d.nim
import c

type D* = enum
  Left, Back

when isMainModule:
  assert Left is D

Backwards Compatibility

Backwards incompatible for projects already using overloadableEnums however the fix is extremely adaptable and simple.

If pure is used, the behavior of the old pure would be removed, but this can still only be under --experimental:overloadableEnums.

Araq commented 2 years ago

The problem with this proposal is that it keeps two distinct forms of enum in Nim. Ideally there is only one enum construct that just works.

metagn commented 2 years ago

Should these tests be removed then? Keeping the tests means we would need to keep some way to opt in/out of overloadableEnums, meaning there would still be 2 enum constructs.

al6x commented 2 years ago

I dislike this proposal. Overloadable enums looks like a proper way enums should work. It should not need any annotations, it should just work, by default.

Araq commented 2 years ago

Should these tests be removed then?

Yes.

metagn commented 2 years ago

Should these tests be removed then?

Yes.

Done in https://github.com/nim-lang/Nim/pull/20298