will correctly infer the return type as an MMatrix. The final type parameter (the 9) is the number of elements stored for the MMatrix, and the T is the numeric type (here, a Float64)
The second method overwrites the first here, so you’re studying the bar(mfoo) version in both cases. It looks type stable when the argument is an SFoo because the compiler knows it will error and never return; instances of type SFoo have no field MGnu. Note that the inferred return type is Union{}, which is a type with no instances.
To implement different methods for different types, use type annotations as follows:
function bar(sfoo::SFoo)
sgnu = sfoo.SGnu
return Adjoint(sgnu)*sgnu
end
function bar(mfoo::MFoo)
mgnu = mfoo.MGnu
return Adjoint(mgnu)*mgnu
end
To solve the type stability issue, make the fields concretely typed as shown by @JonasWickman, though I would personally prefer a solution that requires less knowledge about the implementation of MMatrix, like the following:
One more thing, you should normally avoid using the constructor Adjoint. Instead, use the function adjoint, or, equivalently, the postfix operator ', as in adjoint(mgnu) * mgnu or mgnu' * mgnu.
The main difference is that adjoint knows that it is its own inverse, so adjoint(adjoint(M)) === M. Meanwhile, Adjoint is a dumb wrapper and will keep wrapping the wrapper if called multiple times.
My limited understanding is that bar(sfoo) and bar(mfoo) are distinct by dispatch of the argument. Are they not?
Extending MMatrix{3,3} to MMatrix{3,3,Float64,9} does remove the type instability. I would like to beter understand why extending {3,3} to {3,3,Float64,9} is not required in the SMatrix case.
You need to add type annotations, as showed in my post above. Julia dispatches on types of arguments, not names of arguments.
As explained, if you actually tried to call bar(sfoo) the way you’ve written it, it would throw an error every time. This is why it looks “type stable”—it never returns and has no return type. Once you have a working implementation for both cases you will need a concrete field type in both cases, for example using parameters {3,3,Float64,9} (but see also the simpler and more generalizable solution at the bottom of my first post).