Hi all!
I'm trying to remove some prefixes from the names of procedures in my GBA library.
Currently a "hello world" program looks like this:
import natu
irqInit()
irqEnable(II_VBLANK)
tteInitChr4cDefault(bgnr = 0, initBgCnt(cbb = 0, sbb = 31))
tteWrite("Hello world")
while true:
VBlankIntrWait()
I want it to look like this:
import natu
irq.init()
irq.enable(II_VBLANK)
tte.init(bgnr = 0, initBgCnt(cbb = 0, sbb = 31))
tte.write("Hello world")
while true:
VBlankIntrWait()
i.e. simplify all the names and use the module name to disambiguate.
The library is already split into several modules, which are forwarded from natu.nim via export:
import natu/private/[
types, memmap, memdef, bios, core, input, irq,
math, oam, surface, tte, video, reg
]
export
types, memmap, memdef, bios, core, input, irq,
math, oam, surface, tte, video, reg
Unfortunately my desired hello world seems impossible with this approach, because export somemodule does not make the symbol somemodule usable in the importing file.
Here's a minimal example: I want to do b.foo() but I'm forced to do a.foo()
a.nim
import b
export b
b.nim
proc foo*() = echo "hello"
main.nim
import a
a.foo() # works, but not what I want
# b.foo() # doesn't work :(
Workarounds
Here are some solutions I've considered:
import natu/[core, input, video, math, irq, tte, bios]
This sucks because it introduces needless extra overhead for developers. You have to deal with unused import warnings (or disable them), you have to learn where everything comes from and constantly scroll up to edit the top of the file.
import natu/[common, irq, tte]
Most modules don't really have the prefixing issue, irq and tte are the troublemakers, so I could just split those out and keep everything else in the core module. This kinda solves it, but the division feels pretty arbitrary - these modules aren't any more or less important than input, math, video etc. Also the problems of the previous point still apply somewhat.
include natu/common
# equivalent to:
# {.warning[UnusedImport]: off.}
# import natu/[core, input, video, math, irq, tte, bios]
Works but I'd rather not force include upon my users...
import natu/private/tte as m_tte
const tte* = TTEModule()
proc init*(module: static[TTEModule]) = m_tte.init()
This seems like over-engineering. It clutters jump-to-definition, nim doc, etc. The old prefixed functions were better than this.
In summary, I want export mymodule to make the symbol mymodule available to the importing code.
Is this something that the language could support in the future?
Is there a recommended workaround?
Thanks!
in my opinion 1st solution is fine and overhead isnt that big.
you have to learn where everything comes from and constantly scroll up to edit the list at the top of the file.
you will learn where stuff is anyway when you're reaching for docs and you dont have to edit imports constantly. not an issue.
Thinking about it more, I'm inclined to agree that the separate modules is something you can get used to. I will probably use that approach.
However, the fact that the tte and irq modules are not "forwarding-friendly" is still an issue. For example, a developer might want to make a project-specific "common" module which exports everything they frequently use. Then they run into the exact same problem.
I found a slightly nicer approach to idea 4... I'm honestly tempted to use it just to make this forwarding problem go away.
irq.nim
type FnPtr* = proc() {.nimcall.}
proc init*(fn: FnPtr = nil) =
## set up the master interrupt handler
discard
proc enable*(id: IrqIndex) =
## activate an interrupt
discard
proc add*(id: IrqIndex, fn: FnPtr) =
## register a listener for a specific interrupt
discard
# workaround for module forwarding:
type irq* = distinct void # dummy type acting as a namespace
# normal solution:
template init*(m: typedesc[irq], fn: FnPtr = nil) = init(fn)
template enable*(m: typedesc[irq], id: IrqIndex) = add(id)
template add*(m: typedesc[irq], id: IrqIndex, fn: FnPtr) = add(id, fn)
# lazy solution:
#template init*(m: typedesc[irq], args: varargs[untyped]) = init(args)
#template enable*(m: typedesc[irq], args: varargs[untyped]) = enable(args)
#template add*(m: typedesc[irq], args: varargs[untyped]) = add(args)
mylib.nim
import natu/private/irq
export irq
main.nim
import mylib
irq.init() # ok