to call a function it is necessary to know how (e.g. how should the parameters
or the return value be transfered). This is what's called a calling convention.
In practice you don't have to worry about this much, except when interfacing C
or when dealing with closures like here.
A reference to a closure is a bit different to a normal reference to a proc.
Usually a single pointer pointing to the memory address where the proc begins
is enough information to call it. But a closure is special because it's not
only a proc, but it also has data associated with it captured by the proc.
Here's an example of a closure:
proc test(x: int): proc(): int =
(proc(): int = x)
Run
As you can see, the returned proc changes it's behaviour depending on the
context it was created in, thus it needs two pointers two store the function.
It's not always desirable to have this overhead, this is why procs which don't
capture anything are set to the non closure calling convention (like start and
update in your example). It is possible to store a reference to a non closure
proc in a closure proc, the second pointer then is just set to null. Before
calling a closure it is checked whether the data pointer is null or not, if yes
it uses the non closure calling convention (nimcall) otherwise it uses the
closure calling convention. The other way around is of course not possible.
As I said it's possible to have references to procs with the nimcall calling
convention saved in references in the closure calling convention. This
conversion happens automatically for singular values like in the example where
you show what's valid, but it doesn't work with tuples. You can perform the
conversion before before packing the proc references into the tuple, like this:
((proc())(start),(proc())(update))
Run
For more informations check out the manual:
[https://nim-lang.org/docs/manual.html#types-procedural-type](https://nim-lang.org/docs/manual.html#types-procedural-type)
I hope this a bit lengthy explanation helps.