Dispatch function based on values of a parametric structure

Hello everyone,

I am using a parametric structure parameterized by an integer parameter p.

Depending on the value of p, the function f will have different behaviors.
What is the best solution: write an if elseif else structure inside the function f or dispatch f according to the value of p.

If I choose this second option, how should I write the dispatch? I have something like this in mind but this is not the right syntax.

struct mystruct{p}
A::Array{Float64,2}
end


f(m::mystruct{p}) where {p==0}
# do operations
end

f(m::mystruct{p}) where {p>0}
# do different operations
end

Just write the conditional inside f.

What is the reason to parameterized on p instead of not just storing it in the struct?

2 Likes

Sorry to digress slightly: I was under the impression that the p in the struct definition must be a DataType. Yet

p1 = mystruct{1}([1.0 2.0])

does not throw an error. Since dispatch on p==1 is not possible, what would be a use case (and is it documented somewhere; I could not find it)?

Thanks.

What do you mean? It is definitely possible. Just look at e.g. StaticArrays.jl

I had not seen that type of dispatch before. Thanks.

I just give this configuration with one parameter as a toy problem. In reality, I have 3 parameters to parameterize my structure.
Instead of making a long function with all the different possible combinations, I ‘ve thought that creating a function dispatched according to these cases will be more efficient than nested if conditions.

Surely you’ve seen dispatch on Array{., 1} vs Array{., 2}?

1 Like

It’s not really about efficiency, but about convenience. You can write it whichever way you want, just do it whichever way you think is nicer and gives cleaner, more readable code.

If methods for different parameters share a lot of code, maybe it’s easier to write them in a single function body. If not, it may be better to write different methods.

You can do it like this (note that typenames should be CamelCase, not lowercase):

function f(m::MyStruct{0})  # method 1
    # do operations
end

function f(m::MyStruct{p}) where {p}  # method 2
    # do operations
end

You cannot do arithmetic or comparisons such as p > 0 on the type parameter in the function signature, that has to happen inside the function body. Here, p==0 dispatches to method 1, and then every other value for p dispatches to method 2.

2 Likes

Perhaps the most common use is for AbstractArrays:

foo(A::AbstractArray{T,1}) = # definition for vectors
foo(A::AbstractArray{T,2}) = # definition for matrices

Regarding efficiency, because these numbers are known at compile time, the compiler will get rid of all the branches like if p > 0 (leaving only the code for the branch it would’ve taken).

Meaning they should perform identically if you’ve avoided all dynamic dispatches.

It is always recommended to check the docs first:

Both abstract and concrete types can be parameterized by other types. They can also be parameterized by symbols, by values of any type for which isbits returns true (essentially, things like numbers and bools that are stored like C types or struct s with no pointers to other objects), and also by tuples thereof. Type parameters may be omitted when they do not need to be referenced or restricted.

1 Like

Yes - I clearly failed to connect some dots here. I thought of array dimensions as a special case, but had not seen a struct parameterized by just a number.

I don’t want to divert the topic from the original question. Thanks for all the responses though.

Thank you for all your answers, this helps me a lot.