Hello!
Say I have many different concrete types and I want to specify a method that is dispatched on the set of types A,B,C,D all different, but in an order agnostic way, is there no better option than to specify f(::A,::B,::C,::D) and then specify all other possible combinations by then calling that method?
This would imply writing 4!=24 different method definitions...
You could define const ABCD = Union{A, B, C, D}
and use f(a::ABCD, b::ABCD, c::ABCD, d::ABCD)
and then do a runtime check of the times. I imagine you would have to sort the variables somehow internally anyway in this case?
No but in this case it would match a call to f(A,A,A,A) which I do not want, I only want to match when all are different
you can do typeof(a) !== typeof(b) !== typeof(c) !== typeof(d) || throw(ArgumentError("types not distinct"))
in your method
Yea, but you can check for that and error is what I meant.
Sukera said:
you can do
typeof(a) !== typeof(b) !== typeof(c) !== typeof(d) || throw(ArgumentError("types not distinct"))
in your method
or lets say I want something else to happen for f(A,A,A,A).. i.e definately not dispatch and throw an error
Then you define another method with that signature. It will be more specific than the union.
aha yes!
Thanks!
I've created a macro for this:
using MacroTools
using Combinatorics
function applyPerm(perm,vector)
newVector=copy(vector)
for (i,j) in enumerate(perm)
newVector[i]=vector[j]
end
newVector
end
function invertPerm(perm,vector)
newVector=copy(vector)
for (i,j) in enumerate(perm)
newVector[j]=vector[i]
end
newVector
end
invertPerm([2,3,1],applyPerm([2,3,1],[1,2,3]))
function combineargs(arguments)
[combinearg(arg...) for arg in arguments]
end
def = :(
function t(p,t,s::A,[a::A,b,c::B,d::D])
@info "Abcd"
end
)
macro symmetric(def)
dict=splitdef(def)
arguments=map(splitarg,dict[:args])
symargs=[]
indices=[]
nargs=0
newarguments=[]
for arg in arguments
if isexpr((arg[1]),Symbol)
push!(newarguments,arg)
nargs+=1
else
addsymargs=(splitarg.(arg[1].args))
push!(newarguments,addsymargs...)
nnewsymargs =length(addsymargs)
push!(indices,(nargs+1:nargs+nnewsymargs)...)
nargs+=nnewsymargs
push!(symargs,addsymargs...)
end
end
arguments=newarguments
dict[:args]=combineargs(arguments)
argtypes=map(x->x[2],symargs)
perms=collect(multiset_permutations(argtypes,length(argtypes)))
n=length(argtypes)
m=length(perms)
MATCHED = gensym()
intperms=Array{Int}(undef, m,n)
for (i,perm) in enumerate(perms)
for (j,type) in enumerate(argtypes)
k =findfirst(x -> x===type,perm)
perm[k]=MATCHED
intperms[i,j]=k
end
end
fns=Vector{Expr}(undef,length(intperms[:,1]))
fns[1]=esc(combinedef(dict))
for (i,perm) in enumerate(eachrow(intperms[2:end,:]))
cdict=copy(dict)
carguments = copy(arguments)
carguments[indices] = applyPerm(perm,arguments[indices])
argu =map(x->x[1],arguments)
body=:($(dict[:name])($(argu...)))
cdict[:body]=body
cdict[:args]=combineargs(carguments)
fns[i+1]=esc(MacroTools.combinedef(cdict))
end
return quote $(fns...) end
end
struct A end
struct B end
struct C end
struct D end
@symmetric function p([a::A,b,c::B,d::D])
@info "Abcd"
end
p(A(),A(),C(),D())#method not found
p(A(),B(),D(),C())#method found!
However this is my first macro so I this I might have gotten some things wrong. When the method isn't found I get an error: could not determine location of method definition
, Could someone more experienced take a look?
I think what you are looking for is diagonal dispatch:
f(a::T, b::T, c::T, d::T) where {T} = "all same"
f(a, b, c, d) = "not all same"
The thing I want to avoid is to have to define methods for all the different orderings of the types.. So while diagonal dispatch eliminates the all equal case, it doesn't really solve the problem. The macro above actually works quite well, except this weird error thing..
I guess what I want is to be able to dispatch on sets of types, not specific orders of types.
Last updated: Nov 06 2024 at 04:40 UTC