The problem arises in the context of distributed computing. I'm actually following a suggestion you came up with in this thread. To make distributed computing work at all in native languages, the client (aka driver) and the workers need to run the same binary. My first working draft of the low-level API looks like this (code on GitHub):
# testapp.nim
import math
import runner
import driver
import remote
# registerSerializer and remote are macros that expand to
# additional runtime registration code.
registerSerializer(float)
registerSerializer(int)
registerSerializer(seq[int])
proc remoteHelloWorld(i: int, data: seq[int]): float {.remote.} =
echo "I'm running on a worker"
echo i
echo data
result = sum(data).float
proc remoteApp(ctx: Context) =
# Arguments need to be pushed to the the key/value store of the workers
ctx.pushData("i", 42)
ctx.pushData("data", @[1, 2, 3])
# Remote function call stores the result under a new key
ctx.remoteCall(remoteHelloWorld, @["i", "data"], "result")
# Now we can fetch the data from the worker
let result = ctx.pullData("result", float)
echo result
launch(remoteApp)
launch provides a command line interface to determine the role, i.e., you would run distributed instances via testapp master, testapp worker, testapp driver. The important thing is that all involved parties have a same understanding of registered remote procs and serializers so that they can communicate internally based on proc/serializer IDs. As long as registerSerializer and {.remote.} are only used during module initialization this should work, because master/worker/driver will execute the same code. As soon as the client code would put them into a local scope, the workers would not know about the IDs, leading to a runtime error. An alternative to using module-scope-runtime-registration would be to solve the registration entirely at compile time. As far as I can see this would require to run some kind of compile-time-finalization, because e.g. registerSerializer could be used from multiple modules and I don't know how to "freeze" the set of all registered types to build a lookup table.
The whole use case is definitely exotic and pushing any native + statically typed language to its limits, but I'm surprised how well it works so far in Nim.