Proposal for a first-class dispatch wrapper

Following the discussion generated by https://github.com/JuliaLang/julia/pull/19801, I think that the world-capturing behavior of that proposal was probably sub-par (complex to implement and use). I think the following is instead a much more useful model. For those following along with that issue, this is also intended to be a very close model for how I think the cfunction itself should also behave.

# a Callback is just a callable (plus a dispatch cache)
immutable Callback{ReturnType <: Type} <: Function
    func::Any
    cache::Ref{SimpleVector}
    function Callback(func::ANY)
        return new(func, Ref(svec()))
    end
end

# allow implicit conversion of Any -> Callback{T}
# and Callback{T} -> Callback{S} where S >: T
eltype{RT}(::Type{Callback{RT}}) = RT
convert{C <: Callback}(::Type{C}, x::ANY) = C(x)
convert{C <: Callback}(::Type{C}, x::Callback) =
    if (eltype(x) <: eltype(C))
        return C(x)
    else
        throw(TypeError(:Callback, "convert", eltype(C), eltype(x)))
    end

function (at::Callback{RT})(args...) where RT
    # this implementation is pseudo-code,
    # it would be actually implemented as an intrinsic
    # since in general I think this can be inlined and
    # optimized very effectively
    push(tls-world-age = world-counter)
    # during the following dispatch,
    # the lookup result may get cached
    # as `svec(Method, argtypes...)`
    # for faster results
    local retval = at.func(args...)::RT
    pop(tls-world-age)
    return retval
end

I find this to be much simpler in all of concept, implementation, and usage. This also serves to wrap an arbitrary object in a callable, and has optimization behavior figured out by the callee (rather than needing to be declared by the user).

This makes the above examples much simpler:

# type SomeType; callback::Callback{Any}; end
register_callback(func::ANY) = (this.callback = func; nothing)
do_callback(this::SomeType, args...) = this.callback(args...)
functions = Any[+, -, *, /]
arrows = [ Callback{Float64}(f) for f in functions ]
[ arrow(1.0, 2) for arrow in arrows ] # :: Vector{Float64}
function eval_something_newworld(func, args...)
    # return eval(current_module(), Expr(:body, Expr(:return, Expr(:call, QuoteNode.(args)...))))
    return Callback{Any}(func)(args...)
end

(When called from inside a generated functions, they would still work, they’ll just fallback to acting like a normal dynamic dispatch, since for the generated functions, the newest world is the world-age in which it was defined.)