"running in world age X, while current world is Y" errors

  1. No, simply defining the same function twice wouldn’t cause this — you can try it by modifying the task example I have above. In your case, the problem is likely the same as the simpler example in the documentation: Redefining Methods. You’re evaluating code and defining new methods and then trying to call those new methods, but the code that is doing that is still frozen in the old world from when it started running.

  2. Yes, invokelatest is definitely slower. It has to emit very pessimized code since it cannot assume anything about the function it calls (it can change at any point). This means no static dispatch, no inlining, and no type inference for downstream optimizations. So use it only when you need to. That said, it’s totally reasonable to use when necessary — it’s precisely how the REPL works.

In general, I find it most instructive to think about this in terms of the valid optimizations inference can make. When you run a new function, inference goes through and looks at all the functions you call as it’s compiled. If everything prior to a given function call is well-typed, it can figure out the dispatch at compile time and avoid that overhead at run-time. Further, it can look into the function definition itself and see if it is small enough to be inlined. It can then recurse and analyze the called function to figure out what the return type will be. This allows the optimizations to continue to the next function you call.

But this all is only valid given the information the system has when it compiles your function. In your case, inference has no idea what this up() function might do or return since it isn’t defined at all yet. invokelatest is your way of telling it that it’s ok and expected, so it should just skip all those optimizations.

2 Likes