Adding methods to a function wrapper dynamically

Good morning from Manila! :wave::coffee:

I have a function wrapper:

julia> struct fwrap
   f::Function
end

julia> fw = fwrap(() -> nothing)
fwrap(#11)

I can now implement multiple methods for my f, e.g.

julia> fw.f(x) = x^2

julia> fw.f(s::String) = s*s*s

Now, I want to dynamically add methods based on arguments that are not known ahead of time. For example, I might be given an array of types [T1, T2, T3] and another function h and I want to add a method

fw.f(::T1,::T2,::T3) = h

Any ideas how I can do this (preferably without using @eval)?

For reference to the actual issue, please see this https://github.com/JuliaWeb/HTTP.jl/issues/226

Thanks :pray:

So do you want a fallback that returns the function object h for any set of argument types for which you haven’t created a more specific method? If that’s the case,

fw.f(args...) = h

would work. But maybe you could explain a little bit more about what you’re trying to accomplish (wasn’t straightforward to gather from the issue you linked); it could be that I don’t understand the problem, or that there’s a different approach that is more appropriate.

As a side note, having f as an abstractly typed field in fwrap (Function is an abstract type) means that all dispatch will happen at runtime, which is bad for performance.

I’m pretty sure that in general you’ll need eval; the only way I know to add methods is by “coding” them.

Without having read the linked issue, maybe this could help:
If performance is not paramount (you should profile) or if JIT-time is an issue, then you could hand-code the dispatch:

struct Fwrap
  disp::Dict{Type,Function}
end
Fwrap() = Fwrap(Dict{Type,Function}())
(fw::Fwrap)(args...) = fw.disp[typeof(args)](args...) # make Fwrap callable
register_method(typs, fw::Fwrap, fn) = fw.disp[typs] = fn

# use it:
fw = Fwrap()
register_method(Tuple{Int}, fw, sin)
fw(5)==sin(5)
register_method(Tuple{Float64}, fw, cos)
fw(5.0)==cos(5)