Some of you may have seen this in yestersdays IRC logs:
18:20:41 FromGitter <kayabaNerve> Anyone here know why {.pure.} was removed?
18:20:57 FromGitter <kayabaNerve> I'd love to force scoping enums...
18:24:11 FromGitter <Araq> yeah, I removed it because it sucked big time
18:24:26 FromGitter <Araq> my rationale is in some github issue
18:26:33 FromGitter <Araq> if you want to treat the users of your libraries like little children, give your enum fields annoying overly long names instead
18:27:01 federico3 urgh
Can that be true?
So we have to replace
type
PoliceAlertState {.pure.} = enum
green, yellow, red
TextMarkColor {.pure.} = enum
green, yellow, red, blue
type
City = object
alertLevel: PoliceAlertState
var detroit: City
if detroit.alertLevel == PoliceAlertState.red:
discard # do some action()
by
type
XPoliceAlertState = enum
PoliceAlertStateGreen, PoliceAlertStateYellow, PoliceAlertStateRed
XTextMarkColor = enum
TextMarkColorGreen, TextMarkColorYellow, TextMarkColorRed, TextMarkColorBlue
type
XCity = object
alertLevel: XPoliceAlertState
var xdetroit: XCity
if xdetroit.alertLevel == PoliceAlertStateRed:
discard # do some action()
You could use a macro:
import macros
macro prefixedEnum(name: untyped, members: varargs[untyped]): typed =
var newMembers = newSeq[NimNode]()
for member in members:
newMembers.add(newIdentNode($name & $member))
result = newEnum(name, newMembers, public = true, pure = false)
prefixedEnum(PoliceAlertState, green, yellow, red)
prefixedEnum(TextMarkColor, green, yellow, red, blue)
type
City = object
alertLevel: PoliceAlertState
var detroit: City
if detroit.alertLevel == PoliceAlertStateRed:
discard # do some action()
But merged-with-prefix form stays at use site (if detroit.alertLevel == PoliceAlertStateRed:). With pure, there were 2 identifiers with dot between, now one long identifier - it's not seen anyhow that "Red" part of "PoliceAlertStateRed" is somehow special to say "State".
An underscore can be used before "Red" to somehow distinguish visually - but that's too artificial, not in Nim style (underscores at all), and really undescores can be in enum items too, so that wouldn't really tell the structure.
This one keeps the type section semantics:
import macros
macro prefixEnums(typeSection: untyped): untyped =
typeSection[0].expectKind(nnkTypeSection)
result = typeSection
for typeDef in result[0]:
let name = typeDef[0]
let e = typeDef[2]
if e.kind == nnkEnumTy:
for idx in 1 ..< e.len:
if e[idx].kind == nnkIdent:
e[idx] = newIdentNode($name & $e[idx])
prefixEnums:
type
PoliceAlertState = enum
Green, Yellow, Red
TextMarkColor = enum
Green, Yellow, Red, Blue
type
City = object
alertLevel: PoliceAlertState
var detroit: City
if detroit.alertLevel == PoliceAlertStateRed:
discard # do some action()
A workaround for enabling dot notation at callsite (though poluting namespace), elaborating on Def's macro:
import macros
macro prefixedEnum(name: untyped, members: varargs[untyped]): typed =
var newMembers = newSeq[NimNode]()
for member in members:
newMembers.add(newIdentNode($name & $member))
result = newStmtList()
result.add newEnum(name, newMembers, public = true, pure = false)
for member in members:
let nm = newIdentNode($name & $member)
result.add quote do:
template `member`(e: typedesc[`name`]): untyped = e.`nm`
prefixedEnum(PoliceAlertState, green, yellow, red)
prefixedEnum(TextMarkColor, green, yellow, red, blue)
type
City = object
alertLevel: PoliceAlertState
var detroit: City
if detroit.alertLevel == PoliceAlertState.red:
discard # do some action()
mratsim's version with dot-usage added:
import macros
macro prefixEnums(typeSection: untyped): untyped =
typeSection[0].expectKind(nnkTypeSection)
result = newStmtList()
result.add typeSection
for typeDef in result[0][0]:
let name = typeDef[0]
let e = typeDef[2]
if e.kind == nnkEnumTy:
for idx in 1 ..< e.len:
if e[idx].kind == nnkIdent:
let member = e[idx]
e[idx] = newIdentNode($name & $e[idx])
let nm = e[idx]
result.add quote do:
template `member`(e: typedesc[`name`]): untyped = e.`nm`
prefixEnums:
type
PoliceAlertState = enum
Green, Yellow, Red
TextMarkColor = enum
Green, Yellow, Red, Blue
type
City = object
alertLevel: PoliceAlertState
var detroit: City
if detroit.alertLevel == PoliceAlertState.Red:
discard # do some action()
Aw, I'm going to miss {.pure.} since I'm such a clean freak. :P
Anyway, it's important that Nim learning resources be updated. We don't want beginners running into problems when they follow examples.
For example, this excellent resource that basically taught me how to program practically in Nim uses {.pure.} and will need to be updated.
Are you sure it's gone? The relevant commit I'm aware about is https://github.com/nim-lang/Nim/commit/2f7b979e384ff6cc750e123939401a72c3f59093 ; which allows both qualified and (when non ambiguous) unqualified access to a pure enum.
See also https://github.com/nim-lang/Nim/issues/8850 where I suggest merging making pure the only type of enum, making pure a noop; it'd have the same semantics: allows both MyColor.blue and (when nonambbiguous), blue
@timothee Thanks for pointing that out! So currently if there are enums with members with same names like:
type
PoliceAlertState = enum
Green, Yellow, Red
TextMarkColor = enum
Green, Yellow, Red, Blue
it throws a Redefinition of 'Green' error.
But when the merged implementation is ready, it will allow same named enum members, but give you an error if you use the same named member without a prefix, similarly as how it already works for procs with the same name from different modules? Is my assumption correct?
Araq, your above statement is not very precise.
To test, I just installed latest Nim devel, expecting that much code including gintro would break, with many name conflicts if pure pragma will be removed. But it seems not to break. So I think you did NOT removed pure for enums. What seems to work now is using the enums without namespace like:
type
E1 = enum
a, b
E2 {.pure.} = enum
a, x
#E3 = enum
# a, y # Error: redefinition of 'a'
var e: E2
e = x
#e = a # Error: type mismatch: got <E1> but expected 'E2 = enum'
e = E2.a
So pure is still useful and working to prevent "redefinition" error! And the fact that namespace prefix is not needed any more for pure enums if there is no name conflict was advertised by you already months ago.
What will the naming convention be now?
Will we still choose a prefixed name like nnk*, or can we drop the prefixes?
Or is it that we disambiguate at the call site if an ambiguity exists?
Yeah, that one.