when not defined(release):
...
which is nice (gives a big visual hint) but not very scalable. Managing dozens of symbols would be nightmare.
Here I suggest to add conditional compilation based on this rule:
If it compiles - use it, if it doesn't - drop it.
Example 1 - conditional compilation of an expression:
# if logger module is imported and contains log() function
# and this takes string parameter use it, otherwise ignore it
??? logger.log("...")
The ??? syntax is one of possible, not the critical part of such feature.
Example 2 - conditional compilation of whole blocks of code:
??? {
... some code here # if the code compiles use this block
} else {
.. some other code # otherwise try if this compiles
} else {
... yet another code # or try this
}
Example 3:
type errors = enum
IOError,
when not (defined NO_MEMORY_ERRORS): MemoryError,
LogicalError
...
case err
of IOError: ... # do something
??? of MemoryError: ... # handle this only if enabled
of LogicalError: ...
Advantages of ``???`` feature:
Example 4 - how ??? would preserve visual structure of the code
proc foo =
bar1()
when defined(bar2):
bar2()
bar3()
when defined(bar4):
bar4()
# versus
proc foo =
bar1()
??? bar2()
bar3()
??? bar4()
Among disadvantages is that it would not be always clear whether given conditional code s used or not and if not then why. I guess IDE may give hints, which part of the expression/code block cannot be compiled.
Realistic examples of use:
- General purpose libraries trying to please everyone. If you have logging the library will follow. If you provide callbacks the library will call them. All done automatically and transparently, no need for C like monstrosities:
#define DO_LOGGING 1 #define LOG_MACRO(s) my_logger(s) ... #if (defined DO_LOGGING) && (DO_LOGGING == 1) LOG_MACRO(".."); #endif
- Inside generic code. A sorted container passed to function is array? Use binary search. Is it a list? Use linear search.
Hypothetical implementation of this feature should detect and reject obvious syntax mistakes (unbalanced parenthesis, etc).
1. You do not have to disable every call to logging function. What you have to do is this:
proc log_debug(msg: string) =
when defined(LOG_DEBUG):
echo "do logging here"
Today's compilers are sufficiently intelligent to optimize out empty calls.
All in all i think ??? is confusing noise. Its not intuitive. Its not obvious what enables it. And it does not scale. What if i want two sets of functionality be toggled independently? What if i need even more? All in all there already is tool for this (when) and its working well.
It already exists:
when declared(strutils.find):
# this strutils has a find op
when compiles($foo):
# foo supports the toString operation
@araq:
It already exists
Yes, but:
- It is shorter - less duplication, less opportunity for typos.
- It does not require programmer to maintain bunch of defines somewhere. I found this very annoying and error prone in other languages. In extreme case there would be not a single define needed and yet a library would accomodate itself to every usage.
- It would scale better:
when declared(foo) and declared(bar) and compiles(x.y) # compiler doesn't guard against typo here var n = foo() + bar() x.y(n) # versus ??? { var n = foo() + bar() x.y(n) } # Brackets are not really needed, it's just what I like.
Imagine a multiplatform library supporting several versions of API and sold by level of functionality (e.g. a middleware). While even the ??? may not be enough here it would scale more than the web of when.
- It may allow to structure code better then when (the big example bellow).
- It would check validity of real code, not just declarations. (I am not sure how much compiles does. Even if it handles everything it would be still code duplication.) I was inspired by type classes, how they define their constraints.
I do not suggest to remove the when, ??? would be just handy shortcut for certain situations.
@rku:
ad 1. - you need to define LOG_DEBUG symbol somewhere (cmdline?) and keep the code in sync with this definition. This is nuissance, to put it mildly. Imagine that you just remove logger function and the rest of the code accomodates automatically. I cannot imagine something more simpler.
ad 2. - when exists but is is someting similar to C's #ifdef (or proposed C++ static_if). Clunky, visually disturbing (this may be good thing sometimes), not very scalable. Imagine multiplatform code using many when. It would be always ugly but with ??? it may be less so (the example bellow). else-else is just the first thing that came to my mind, could be elif, whatever.
when defined(x): when not defined(y): ... else: ... when defined(z): ... else: ... else: when not defined(y): ... else: ... when defined(z): ... else: ... # versus ??? { # logically consistent block handling one use case, # not cluttered with 'when's } elif { # another block for another use case } else { # catch all block if nothing else works } # versus when defined(x) and not defined(y) and defined(z) # logically consistent block handling one use case, # not cluttered with 'when's else: when defined(x) and defined(y) and not defined(z) # another block for another use case else: else: # catch-all block if nothing else works }
ad 3. - yes, it wouldn't be obvious if and why the code didn't compile. Complex web of when is not any better, however. That could be resolved by IDE, it may highlight the part which failed. IMO ??? is more scalable then when. Try to write a snippet with 5 defines, covering all options. It would be the same nightmare as with C preprocessor.
If some code is too complicated then IMO ??? is still better, it forces one to separate code into logical parts. ??? and when could be used together (when for small expressions within larger ??? blocks).
Welcome to the power of Nim:
template `???`(x: untyped) =
when compiles(x):
x
???(echo "foo bar")
@araq:
Wow. I am flabbergasted.
Is it possible to somehow chain blocks like this?
??? (
# some code
) elif (
# other code
) else (
# catch-all
)
That would be all I ever dreamed about.
Edit 1: from the docs it feels it would also hide trivial syntax errors:
??? (echo "foo"() )
Edit 2: is there a way to break compilation of a snippet from inside the code? Something like
??? (
when not defined(x): FAIL_THE_COMPILES_FUNCTION
echo "foo"
)
This could serve as a (less handy) replacement for chained blocks feature.