"Meaning", type-piracy, and method merging

Sure, your module could pass the equivalent of a vtable pointer to f to tell it what definition of sum to use, but doesn’t that require compiling the call to sum inside f as an indirect call? That would be slow. We can easily get separate compilation at the expense of performance by compiling every call site to a dynamic dispatch.

1 Like

no, if f is compiled then f is called , otherwise if it is called from function g() in MyModule
it would be jitted at runtime.

if g() exists in the cache g will be called. you would still need dynamic dispatch in places of type uncertainty.
but you could use this context concept to limit what dynamic dispatch will see, potentially inlining it completely

POC
https://github.com/TsurHerman/MultiFunctions/blob/master/MF.jl

I think I see what you might be getting at; let me take a stab.

Say we have modules A, B, C, D that depend on each other in some way. But in those modules, all definitions of length return Int. So we can infer length(x::Any)::Int. But then module X is loaded, which defines length(::Missing) = missing, so now we invalidate any code that assumed length always returns Int. However, that is too conservative: length(x::Any)::Int is still valid within the universe of A,B,C,D, since those modules don’t use X or Missing. It only becomes a problem if somebody starts using both X and A,B,C,D.

Is that the sort of problem you want to solve?

Anyway I’ll keep an eye on your repo and see if I can eventually understand it.

3 Likes

Yes, philosophically speaking , I treat each module as a stand-alone shared object or more precisely each module has a shared object(.so .dll .dylib) associated with it.

Calling a function from that module calls the compiled version of that function if it exists in the .so.
If it doesn’t exist then jitting is taking place with the context of the calling function. The compiled code of the jitting process belong to the shared object of the calling module.

Here there is some room for optimization for example determining whether calling f from module M in the context of module N is identical to calling it from context M … if so the compiled version will be stored in module M. So that any other module calling f with the same argument types will not have to recompile f .

You can see in my POC , that for example addxy in module Ints Floats Reals or Main , default to an abstract type

MultiFunc{:addxy,MOD,MOD} where MOD is the module name respectively. This means in human words : calling addxy from module MOD in the context of module MOD.

Eventually real code exists only for those type of functions.

It’s not a problem I think, from the context of X
Length(::missing) is :;missing and length any other thing is Int .
Calling length(x) where type(x) != missing is not invalidated.

This is problem anyway, isn’t it?

If we are not able to “stabilize” behavior into shared object(.so .dll .dylib) it means it is volatile in our mental model too.

Will we need to rethink whole model with every new compilation in every new context?

Won’t we end with language unusable for bigger long-term projects?

It depends on how you use the code. If you load the .so into an interactive environment whose purpose is to allow you to change and add methods and see the results, then yes the code is “unstable”. If you load the .so from multiple statically-compiled programs that just call into it, no methods are changing so there’s no problem.

From there, the piece we’re missing is being able to load multiple compiled julia modules with arbitrarily-different sets of methods into the same address space. If use cases for that arise, we can look into developing that capability.

If you want strong guarantees about the behavior of functions, that’s just an inherent issue with dynamic typing.

Usually in development you are in both worlds. From one hand you depend on a large set of .so that you rely on , and on the other hand you have a bunch of .so that you actively research and develop. And it is kinda of progressing … once you are satisfied with a module you go on to develop the next … occasionally but just occasionally going back to “fix” something.
So most of the time you are working on a very limited set of methods and you research changes to the model by changing only a limited set of functions and methods.

This is what I call avoidable “re-jitting”

1 Like