This is suggestion for a feature to make application programming a bit easier.
Good design usually has one interface/namespace intended for public consumption and several helpers around it. These helpers are "for internal use only".
What I suggest is the ability to explicitly define "submodules" by limiting who can use them, with help of a pragma:
# module DB_IO
{.module_can_by_used_by: [DB].} # this makes it a submodule
proc read*(...) = ...
proc write*(...) = ...
...
# module DB
import DB_IO # allowed
....
# module XYZ
import DB_IO # will fail to compile
...
This would give visual clue about the structure of an application and reduce the natural growth of internal dependencies.
I dislike this idea, but you can certainly abuse the include keyword to emulate such behaviour. Imagine these files:
# semi_private.nim
when not defined(use_me_safely):
{.fatal: "Hey, you aren't meant to import this directly! Use base.nim".}
proc say_hello() =
echo "Hello"
and
# base.nim
const use_me_safely = "oh yeah"
include semi_private
when isMainModule:
say_hello()
The effect is that you can compile and run base.nim but trying to compile or import semi_private.nim will fail.
A different approach with a similar effect is to follow the guidelines mentioned in the developer documentation for the babel package manager. It suggests placing your private code in a subdirectory from your public module, like a private subdirectory (but you may call it brouhahaha and it should still work).
I prefer to use a different solution myself. You might have heard of it, but certainly it is quite rare. It's called documentation. Most programmers aren't really curious about the innards of your software, and for them you only have to provide a text or html file named documentation or API which says something along the lines of "only the symbols documented here are to be used, other symbols are for internal/private use and may change without warning on future versions". It's the lack of such documents that makes programmers look into foreign source code and hurt themselves with the sharp borders of your private API.
The few programmers who are not deterred by such sneaky documentation jedi tricks won't be deterred by the include trick or private subdirectory either (or anything else you throw at them). There's no solution for them.
I do not think splitting code up just for the sake of splitting code up is a good idea (even though I often do it).
I do not think splitting code up just for the sake of splitting code up is a good idea
I have exactly opposite opinion here. And I have yet to see a real world application (not a library) where I could rely on documentation.
This little feature would allow a newcomer to quickly discover internal structure of the app and would discourage him to add unwanted dependencies. Nested namespaces have this role in C++.
This little feature would allow a newcomer to quickly discover internal structure of the app and would discourage him to add unwanted dependencies. Nested namespaces have this role in C++
I'm really curious about what you mean with add unwanted dependencies, could you elaborate an example of this? Most of my code, unless I'm planning on it to be reusable by a 3rd party, will be tightly integrated with other structures and globals my program uses, so the type system will disallow taking a random proc directly into somebody else's program.
"This little feature would allow a newcomer to quickly discover internal structure of the app and would discourage him to add unwanted dependencies. Nested namespaces have this role in C++."
Nested namespaces do nothing to prevent someone from just going ahead and using your internal code. Additionally nimrod has non-insane (not transitive) scope for module inclusion, thus if you use a nested module (just a folder called internal or something) in nimrod users will not be able create a dependency on the nested code by using constructs with non-obvious scope (ADL, macros, and templates).
My point on not splitting up code just because was not to say that you should create dependencies willy-nilly. What I was actually saying is that if you have a set of code that naturally depends on each other splitting it up into more modules does not really remove the dependencies, it just reduces code locality.
I'm really curious about what you mean with add unwanted dependencies
I mean situation when new maintainer finds a useful routine for him and calls it from some distant code. This may create new dependency between unrelated app parts and/or reuse code not written with such intention. I did it few times and had to deal with situations when someone else did it. The problem is that such routines are coded with certain app specific preconditions in mind and these preconditions are not documented (usually nothing is).
The suggested feature may help in such situation, making distinction between code intended (and tested) for wide reuse and other code. The distinction is something I never saw in documentation, even if generated.
Private directories feel nice but directory structure may be more complex or already predefined. This also applies to file names.
Nested namespaces
Nested namespaces do not prevent anything but give clear visual clue. What is nested usually smells enough not to be used with abandon.
In my ideal world the ability of a routine to be reused would be some kind of annotation. Anything labeled by say [[tested]] would be callable only from other [[tested]] routines and the compiler would verify these tests exist and cover 100% of routine code.
Going even further, there could be bidirectional links between the code and examples, tests and specification and everything would be manipulated together. This, however, requires some futuristic IDE (mbeddr (http://mbeddr.wordpress.com/) tries to go in this direction).
I still think your proposal is unpractical for the same reason you say people don't write documentation. If people won't write documentation, why would they bother annotating every little piece of code they write, especially those they don't expect other people to use at all? However I think you can enforce the behaviour you want through Nimrod's effect system using tags. Let's see these two files, first coreStuff.nim:
type badForYou* = object
type tested* = object
proc multiHello*() {.tags: [badForYou].} =
for f in 0..5: echo "Hello"
proc coreSayHello*() {.tags: [tested].} =
echo "safe Hello"
Then the example.nim file:
import coreStuff
proc sayHello() {.tags: [tested].} =
coreSayHello()
when isMainModule:
sayHello()
Compiling and running the main example file will work. The user tags (badForYou `` and ``tested) would prevent the end user code in example.nim from calling the internal multiHello proc:
example.nim(3, 24) Info: instantiation from here
coreStuff.nim(4, 28) Error: can have an unlisted effect: badForYou
In the same way, if the library maintainer decided to call that proc instead of calling echo, they would also get the warning:
coreStuff.nim(7, 29) Info: instantiation from here
coreStuff.nim(4, 28) Error: can have an unlisted effect: badForYou
Existing code which doesn't use these user tags (ie. Nimrod's standard library) would pass the tests since it doesn't use any of these user defined tags, which may be good or not.
The user tags are generated in the documentation, if you run nimrod doc coreStuff you will get an HTML file with the tags raised by the procs. If you are looking for finer grained control over the documentation, you might want to run the JSON documentation generator (eg. nimrod json coreStuff). Then you could parse yourself the json and produce a documentation removing the badForYou procs, or highlighting/grouping them in a special way. To simplify tag documentation processing you might want to make pull requests to docgen so that the json output already contains a separate tags and you don't have to parse code yourself in the json.
With regards to unit testing, there is an undocumented unittest module which maybe you could expand to suit your needs using these user tags.
The editor part might be possible too, you could extend Aporia's to support these additional user tags. To make them semiofficial I would suggest creating your own babel package named something like validationTags and then add support to Aporia to detect the usage of this module and highlight source code or do whatever you want it to do, as shown in the mbeddr video.
Unfortunately I don't think there exists code coverage tools yet in Nimrod. But I think you could work around this with macros too. Let's say that for every proc tagged with tested you want to enforce a unit test to have a testXXX proc. Your tester could take the source from the module you want to test and embed it into a temporary test file. The purpose of this would be to have a special macro the parent of all the embedded source code so it can use Nimrod's macro module to get the list of procs, figure out tags, etc. The macro would return a nil statement but would output the names of these procs to standard output, which your tester would capture.
Then the same thing could be done on the testing module, and at least validate that the function exists. Or if your test procs are embedded in the source code, you wouldn't need to do this output trickery and the macro could test itself that all procs match at the end of processing the source code block. Through the use of special defines and includes you could maintain the code and testers separate but still available at the same time to your parent enforcing macro.