Method for Base.* when at least one argument is of a given type

Suppose I want to define a vararg method for Base.* which is invoked when at least one argument is of a given type Foo (for the purposes of this discussion, struct Foo end suffices).

What’s the recommended way of doing that? Is there a possibility to do it in a way that composes with other packages that do something similar?

There is no in-built way.

If you don’t mind defining a ton of methods and can put a reasonable guess on the maximum number of args you need to support, you can use metaprogramming to do so.

I wrote that pattern up in a blog post.

2 Likes

Thanks! That’s what I suspected from looking at the way various packages deal with the question.

IMO the ideal approach would be traits, along the lines of

# generic code

promote_f_type(::Type{T}, ::Type{S}) where {T,S} = T # the default is: whichever came first
traited_f(::Type, args...) = :the_default
function f(args...)
    T = mapfoldl(typeof, promote_f_type, args)
    traited_f(T, args...)
end

# type-specific extension

struct Foo end
promote_f_type(::Type{Foo}, ::Type) = Foo
promote_f_type(::Type, ::Type{Foo}) = Foo
traited_f(::Type{Foo}, args...) = :Foo_dominates

which then make

julia> f("a", "b")                     # :the_default
:the_default

julia> f("a", Foo(), "b")
:Foo_dominates

but I don’t control * or string, so this is not viable.

If the argument types otherwise differ, won’t that be caught in the promotion machinery? You could try to hook/use that to get a known-to-you wrapper to dispatch on, though it’s not exactly a pretty solution.

Can you please make this more concrete with an example? I am not sure what promotion machinery you refer to.

I was thinking of these:

But they’re only for <: Number and some specific functions, so the approach is not applicable in general.

1 Like