I found this approach in Rotations.jl. Why code it this way? Note that AngleAxis is a concrete type, so AA can only be one type.
@inline function (::Type{AA})(q::QuatRotation) where AA <: AngleAxis
w = real(q.q)
x, y, z = imag_part(q.q)
s2 = x * x + y * y + z * z
sin_t2 = sqrt(s2)
theta = 2 * atan(sin_t2, w)
num_pert = eps(typeof(theta))^4
inv_sin_t2 = 1 / (sin_t2 + num_pert)
return principal_value(AA(theta, inv_sin_t2 * (x + num_pert), inv_sin_t2 * y, inv_sin_t2 * z, false))
end
# Trivial type conversions for RotX, RotY and RotZ
@inline function (::Type{AA})(r::RotX) where AA <: AngleAxis
return AA(r.theta, 1, 0, 0)
end
@inline function (::Type{AA})(r::RotY) where AA <: AngleAxis
return AA(r.theta, 0, 1, 0)
end
@inline function (::Type{AA})(r::RotZ) where AA <: AngleAxis
return AA(r.theta, 0, 0, 1)
end
No the signatures look the same AFAICT. These arenât really callable structs.
It is a strange way to write a constructor, but I canât seem to find a difference. This can be used to do certain âAbstractâ constructors but AngleAxis is a struct so Iâm not sure the purpose.
and indeed I can see no difference in how they are working in the context cited above. So I will mark it as correct, but maybe someone will see this and explain the use case more. Thanks!
Iâve seen this around and never found out why people write ::Type{X} in the callable position, it doesnât seem to make a difference:
julia> abstract type Y end
julia> struct X<:Y end
julia> (::Type{X})(::Int) = 1
julia> (::Type{blah})(::Float64) where blah<:X = 1.2
julia> (::Type{blah})(::Float64) where blah<:Y = 2.1 # potentially useful
julia> methods(X)
# 4 methods for type constructor:
[1] X(::Int64)
@ REPL[3]:1
[2] X()
@ REPL[2]:1
[3] (::Type{blah})(::Float64) where blah<:X
@ REPL[4]:1
[4] (::Type{blah})(::Float64) where blah<:Y
@ REPL[5]:1
Julia sees all of these as type constructor methods, and only the one subtyping the supertype seems to do something beyond the 1 type, and that is indeed a pattern that shows up frequently, check methods(Int).
PS also found out that Julia doesnât disallow ::DataType and ::Type in the callable position like it disallows ::Function and ::Any, but those are just as bad.
One possible reason is just ergonomically defining constructors with all concrete parameters? It gives you access to the constructor you need within the function obviously. If parameters are added you donât need to worry about changing constructor signatures.