I've been using Nim for work and play for a few years, but I'm still stumped 
what is the "Nim way" to replace our larger projects. 

##### The Project

This is a scientific simulation C++ OOP modular project with user-contributed 
compilable plugins (but everything is compiled at the same time, so no runtime 
loading). What that means is any module may include definitions from any other 
module to work with that module. We specifically allow tight-coupling between 
modules should a module designer want to use this. This tight-coupling is quite 
the problem, otherwise we could enforce abstraction through composition or 
concepts, so maybe our situation is unique in this respect leading to the 
awkward solution below ( **3.C** )

##### 1) Nim supports limited forward declarations

The naive approach to replace our project is to mirror the C++ use of forward 
declarations for type and function information allowing every module to have 
access to other module's information. But, Nim does not completely allow 
forward declarations.

##### 2) Nim does not allow cyclic imports

Blindly forging ahead with `import` means we run into the cyclic imports 
problem.

##### 3) Unity / All-In-One builds

I often see this as a solution to the 2 previous naive methods: basically put 
all your code in 1 file.

**A)** We could write the entire project in a single (gigantic) file and use 
the experimental codeReordering. Works! But eww. Asking module developers to 
"Cut and paste" their code is awful, and losing organization by file is awful, 
not to mention we lose module-level build caching and must rebuild the entire 
code all the time.

**B)** We can fix the organization problem by using a system of `include` 
statements to construct an all-in-one file at compile time. However, this does 
not play nicely with `nimsuggest`* and the developer experience is worse for 
it, requiring all developers to be expert and have memorized the code base. (* 
because `nimsuggest` is a static analysis tool so ignores `include` statements)

**C)** We can fix the nimsuggest problem by allowing faux modules, where users 
write a nim file as if they're writing a standard module and `import` all the 
other plugins or portions of the codebase as needed. However, as pointed out in 
approach **2** , this will create cyclic imports. To fix this, we use a 
nimscript build file that preprocesses every file's `import` statements, 
stripping it of problematic imports, then constructing an all-in-one build 
file. Thus we get the appearance of forward declarations, the optimization of 1 
define across all binary objects, and `nimsuggest` works! But wow, is this 
hoop-jumping really the "Nim way" for large projects of this nature?... Oh, and 
we have to rebuild the entire code base every time anything is changes :( .

##### 4) Independent Libraries Model

We could force module developers to create entirely independent libraries 
(let's say static libraries) and if they require other types and functions from 
other plugins or portions of the codebase, then they have to include that 
possibly redundant code into their plugin. This would work, but may cause 
binary bloat - and I'm not even sure the vtables would work out linking the 
final executable with the multiple definitions.

##### Conclusions?

All-In-One build, but must recompile the entire codebase for every change. Or 
Indipendent Libraries where each module is standalone and must include all code 
it needs to work, possibly bloating the final executable.

##### Other ideas?

I'm so curious to hear your thoughts!

Reply via email to