type
threads_in_thread_pool = enum
1, 2, 4, 8, OptimalNumOfThreads
start_thread_pool(2)
start_thread_pool(OptimalNumOfThreads)
These numbers would just represent themselves.
Another example, return value with symbol for error added:
type
foo_return_code = enum
1 ... 1000, Failure
proc foo : foo_return_code = ...
One more suggestion: right now all enums have to be defined in type section visible to everybody, even when it is intended for one place only and its use elsewhere would be wrong.
Could such limited use enum be defined as part of function definition?
proc foo : enum This, That = ...
proc bar(enum 1, 2, DefaultValue : parameter) = ...
Only caller of the function and the function itself would be able to use these symbols.
Having numbers be used as identifiers for an enum is confusing. It would be more pragmatic to have a converter that takes (static?) integers and converts them to enumeration values.
As for inline enumerations, what's wrong with just marking the enum type as private, and/or putting the definition near the procedure it's used in?
Confusing
There are quite a few situations when one mostly uses numbers but also has a need for one or few symbols. Frequent solution is to use magic numbers (NULL, -1, 0xFFFF). This IMHO spreads confusion accros codebase. (Quick, does ferror() return -1 or 0 on error? Does tmpfile() do the same?)
converter
I do not exactly know how it would look like but to me it sound as something very complicated. Magic numbers feel simpler.
enumeration private or near procedure
Being private doesn't prevent reuse within module IIRC. Placing it near the procedure is just a hint.
Inlined enum would be unusable for others, no matter what how much they try. (I consider reuse bad, it leads to uber-enums and then nobody will never know why this function doesn't handle existing symbol X, is it bug or intentional?)
Sometimes it would be handy to mix numbers and symbolic values.
I think I would like it, when it is not very complicated to get.
You wrote about mixing numbers and symbols. I wonder: When we have only some special valid number like 0, 2, 4, 8 as valid proc parameters now, is there a simple solution currently? Or do we have to use int and check if value is valid in the proc?
I don't think these are good ideas.
About the first one, it would mess with type inference (val x = 1 - is it a int or a threads_in_thread_pool?). Also, it is pretty simple (and more explicit) to just write
type
threads_in_thread_pool = enum
oneThread = 1, twoThreads = 2, fourThreads = 4, eightThreads = 8, OptimalNumOfThreads
I would also be confused if I changed the line
config.numThreads = 4
to
config.numThreads = 6
and have it not compile. Why are 6 threads not allowed? Having an enum would be much easier to search.
About the second idea, it would introduce yet another notion of scope for just a very limited use. I think pure enums and privacy modifiers are more than enough for this use case
@Arrrrrrrrr
Magic number is the (-1) used to indicate error, Or 0. Or anything negative. Or something else, always inconsistently (even in the C stdlib).
This special value would be replaced by a symbol, other numeric values would be left intact.
It would prevent errors like:
int res = ferror(f);
if (res == 0) { // oops, the magic value is < 0
// error handling
}
proc get_number_of_processors() : mixed_value 1..128, CannotFind = ...
let n = get_number_of_processors()
if n == 0: # fails to compile
if n == -1: # fails to compile
if n <= 0: # fails to compile
if n == CannotFind: # OK
Btw with templates you have a different approach to your problem:
type PoolOptions = enum Optimal, Fast, Auto
proc threadPool(threads: int | PoolOptions) =
when threads is int:
# create thread pool with x threads
else:
# more precise tuning
@Arrrrrrrrr
It feels a bit too complicated to me.
Is your solution as fast as the hypothetical mixed value (single integer, no runtime overhead at all)?
I do not understand the "with templates" part.
IMHO the clearest and most elegant approach would be extending enum to accept numeric (anonymous) values or ranges eg.:
proc setPowerOfTwoThreads(num: enum 1, 2, 4, 8, Auto)
proc getNumberOfProcessors() : enum 1..128, CannotFind
However as a quick workaround we can just define some constants for special values:
type NumOfThreads = 0..256
const OptimalNumOfThreads = 0
type PoolOptions = enum Optimal, Fast, Auto
proc threadPool(threads: int) =
# create thread pool with x threads
proc threadPool(threads: PoolOptions)
# more precise tuning
@andrea:
Hmm, two functions, plus some risk that an enum value will by accidentally fit valid numeric value. People would choose magic numbers, I predict.
@andrea:
You are right, enum values are safe. (I was immediatelly thinking about common helper function which would be called by those int/enum wrappers, with parameter cast to int.)
I missed such feature (invocation scope) for a long time, in different languages. For me it would be one of the best Nim features. "Other languages don't do it" doesn't seem to be a reason to deny it, some language should be first.
Mixed enums (named + numeric) should be convenient too.