type
TDirection = enum
TD_north, TD_east, TD_south, TD_west
is not really nice. Putting each enum in its own file is also not really nice.
And another question for enums. For GTK3 I have a few numbered enums with repeated values, i.e. Button2Click=7, DoubleClick=Button2Click. I gues the only solution is to remove one of these?
You can already do this. Just add the {.pure.} pragma:
type
Direction {.pure.} = enum
north, east, south, west
echo(Direction.north)
For repeated values in enums you can move them into a const:
const DoubleClick = Button2Click
You can already do this
Great. I think I should do that for GTK3 enums, like Ruby does. I have just seen that for GDK3/GTK3 there is some inconsistency with enums, most fields have a prefix already from the C headers, but a few not like
type
TEventType* {.size: sizeof(cint).} = enum
NOTHING = - 1, DELETE = 0, DESTROY = 1, EXPOSE = 2,
type
TModifierIntent* {.size: sizeof(cint).} = enum
MODIFIER_INTENT_PRIMARY_ACCELERATOR, MODIFIER_INTENT_CONTEXT_MENU,
I think I will use that pure pragma for all GDK3/GTK3 enums to enforce the prefix and remove the textual prefix from the individual fields -- hope I can do that with a little Ruby script...
dom96: {.pure.} pragma
Isn't pure a misnomer? Function can be labeled pure (no side-effects) but an enum? Why isn't something as {.requires_prefix.} used instead?
Araq: C++ uses terms scoped and strongly typed. If one word is needed I'd suggest prefixed . (I feel pragmas as advanced comments which could be wordy, unlike keywords.)
When I am at renaming: the pseudo-functions likely and unlikely from system library would IMO be more naturally spelled as pragmas (say {.likely_path.} and {.unlikely_path.}).
OTOH, the superfeature {.destructor.} deserves its own keyword, not just a pragma. When skimming the code it is too tempting to skip pragmas.
I also have one feature suggestion for enums: implicit safe cast.
I use error codes for error handling (exceptions are evil; and no, it is not duplicating compiler's work). I write code like this:
# module 1
type func1_err {.pure.} = enum OK = 0, Error1 = 1, Error2 = 2
proc func1(...) : func1_err = ...
# module 2
type func2_err {.pure.} = enum OK = 0, Error2 = 2, Error3 = 3
proc func2(...) : func2_err = ...
Then I have a common error handler:
# another module
type every_error {.pure.} = enum OK = 0, Error1 = 1, Error2 = 2, Error3 = 3, ...
proc error_handler(every_error: err) = ...
I'd like the to use it this way:
let res_1 = func1(...)
if res_1 != func1_err.OK: # ideally full qualification would not be needed
error_handler(res_1)
let res_2 = func2(...) # I'd love to reuse res_1 ("flow typing")
if res_2 != func2_err.OK:
error_handler(res_2)
but different enum types prevent this.
It can be handled by:
What I would like is automatic cast to different enum when:
These two rules would make the implicit cast safe against typos and one-sided changes.
For traditional error handling this little feature would find good use.
An alternative syntax could be something like this:
error_handler(!res_1) # the ! indicates implicit safe cast
When I am at renaming: the pseudo-functions likely and unlikely from system library would IMO be more naturally spelled as pragmas (say {.likely_path.} and {.unlikely_path.}).
Agreed.
OTOH, the superfeature {.destructor.} deserves its own keyword, not just a pragma. When skimming the code it is too tempting to skip pragmas.
Agreed.
I also have one feature suggestion for enums: implicit safe cast.
Just use a converter.
I use error codes for error handling (exceptions are evil; and no, it is not duplicating compiler's work)
Nimrod's exceptions are not that evil since they are part of its effect system. And yes, error codes for error handling are horrible and you're duplicating compiler's work. Using ifs everywhere is also slower than using exceptions properly. Exceptions also can make a program easier to reason about, but this requires its own blog post ;-) That said, error codes work even better in Nimrod than in Golang because of Nimrod's discard enforcement.