Adding methods to a function wrapper dynamically


#1

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:


#2

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.


#3

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)