1. Scoped/local imports:
import xyz
#...
if (abc == def):
echo (xyz.method)
In the example above, if the condition is never met importing xyz is wasteful. However if local imports were supported, the "import xyz" portion could be done under the if block.
Basic imports work well for programs with relatively few modules and imports. If there are a lot of imports, name collisions can start occurring between the names in the various imported modules. One way to stop this is by using static imports. A static import requires one to use a fully qualified name to reference the module's names.
static import math
echo E # error, E is not defined
echo math.E # works
Should these be/are these supported?
For 1, if the condition is available at compile time (generally this means abc and def are const) you can use a when block to import the module, eg:
when cond:
import foo # this is only imported if `cond` is true and evaluable at compile time
if cond:
foo.bar()
This is a conditional import, and imports can only be at top level so for example you can't import something inside a proc. I don't think importing a module at runtime is possible at all.
For 2, you can use from math import nil to force fully qualified names ( see here )
As @stisa mentioned above, runtime imports are not really possible in Nim because Nim is not an interpreted language. Runtime imports are possible in a language like Python because the CPython VM can stop execution in a scope to import a module. Nim, however, pulls in modules at compile time. Once Nim code is compiled into an executable binary, there is no way to include a module without recompiling. The when conditional above is like an if statement that runs at compile time. Therefore, it is capable of conditionally importing modules.
In addition to @stisa's comments regarding qualified module name resolution: In most cases there is actually no need for namespaced modules as demonstrated because Nim performs function resolution based on a function's type signature. Very rarely are there two separate functions in 2 separate modules that are named the exact same thing, take the exact same arguments, and return the exact same value.
well importing in a local block/function has nothing to do with runtime importing. Just see scala import.
https://en.wikibooks.org/wiki/Scala/Import
and yes for your case use a when clause. The content of a when brach is at top level it does not create a new scope.
if the condition is never met importing xyz is wasteful
Yeah, unused imports still increase binary size despite deadCodeElim (--d:release --opt:size). In a perfect world that would not be the case...
you can use from math import nil
Yup. But I still think Nim needs better syntax sugar for doing this, since preference for old-fashioned mandatory namespace prefixes are such a common complaint (ex). Writing from blah import nil for every import is ugly. One is tempted to do something like:
macro load*(modulesSeq: varargs[untyped]): untyped =
result = newStmtList()
for module in modulesSeq:
result.add parseStmt("from " & $module & " import nil")
load os, math
But doing this through an ad-hoc macro has some down-sides. @Araq, how about adding a built-in keyword like that before v1.0? ;)
If so, we should discuss if the keyword name load is ideal. I first thought about using use, but that would remind people of Perl, while import reminds people of Python - this would be topsy-turvy of how those two languages behave regarding mandatory namespace prefixes. It's of course too late to rename the import keyword. So it would mean: "not just load, but also import into current namespace".
runtime imports are not really possible in Nim because Nim is not an interpreted language
Nitpick: Nim is a language that S2S-compiles to a "not interpreted" target (C, a machine code targeting language), by default. But it can also compile to interpreted languages as well (JS now, maybe others later).
Perhaps one could create a library for runtime imports, which would work differently on different targets: JS, so/dll, etc. (It would probably still need to read the imported code and generate headers at compile time.) But that's a whole nother opera.
Yeah, unused imports still increase binary size despite deadCodeElim (--d:release --opt:size). In a perfect world that would not be the case...
How so? That needs to be fixed then.
@Libman Oh man, I've spend this whole day learning about how macros in Nim work only to recreate the exact same macro you've listed. Only I used require for its name.
Too bad it doesn't read args from the next line like usual sections can.
How so? That needs to be fixed then.
Writing from blah import nil for every import is ugly
Probably less idiomatic/encouraged way should not be absolutely the same easy, as more idiomatic. So two extra words for the whole imports list should be OK, it could be like this: from xxx, yyy import nil.
(My previous post was posted before mapi's post became visible due to new account moderation.)
Only I used require for its name.
The three related keywords (import, include, and this proposed new one) should be easy to remember which is which.
The same use vs import distinction is used in Elixir:
# Import functions from Foo so they can be called without the `Foo.` prefix
import Foo
# Invokes the custom code defined in Foo as an extension point
use Foo
Could the {.pure.} pragma be made to work with imports the same as it works with enums?
Example:
import math {.pure.}
echo cos(1.0) # Error
echo math.cos(1.0) # OK
This is a great macro you made @Libman:
macro load*(modulesSeq: varargs[untyped]): untyped =
result = newStmtList()
for module in modulesSeq:
result.add parseStmt("from " & $module & " import nil")
# Usage example
load os, math
dirExists("/") # Error!
os.dirExists("/") # OK
I find this macro horribly useful! It doesn't have to be load, I do not care what its name is.
Could we put it in one of the standard library modules? future module maybe?
It might look useful for simple cases
Which is what people do the majority of the time, especially when getting acquainted with a new language. Simple things should be simple and clean.
but what happens when you want to import an operator? Or if you want to import a hash procedure that the tables module should have access to?
Same as with from module import nil. This isn't always desirable, so the user would choose what to do when things get complicated. (Or an IDE can be told to automatically generate import statements for the things used.)