Given q = one(Quaternion{Float64}), it seems to me that eltype(q) === Float64 makes the most sense. Given the second half of the help text:
Determine the type of the elements generated by iterating a collection of the given type. For dictionary types, this will be a Pair{KeyType,ValType}. The definition eltype(x) = eltype(typeof(x)) is provided for convenience so that instances can be passed instead of types. However the form that accepts a type argument should be defined for new types.
Base.eltype(::Type{Quaternion{T}) where {T<:Number} = T makes sense imo. Also Base.eltype(::Type{QuaternionAsVector{T}}) = T.
My understanding has been that a pure quaternion is a quaternion. Implementing real(q) and imag(q) makes sense. Would you have imag(q) return a vector or a tuple? Do you agree that real(q) should return a scalar?
Good questions. For the application I have in mind I actually would want both to end up as quaternions, since I’m feeding everything into a sparse linear system and the uniformity of dimensions helps book-keeping. But that’s related to my specific use case/implementation. Constructors with 1 and 3 airty could be used to easily convert.
eltype(Quaternion{Float64}) === Float64 is only okay if and only if collect(q::Quaternion{Float64}) == [q.w, q.x, q.y, q.z] (modulo the field order and field access), and I believe also iff 1 .+ q == [1 + q.w, 1+q.x, 1+q.y, 1+q.z] (to say something about broadcasting).
I don’t think it should be done, and that the Quaternion type (or any type that implements the quaternion interface) should behave like Complex and all the types defined in ColorTypes.jl. That is, they should be treated like scalars for the purpose of iteration, indexing, and broadcasting.
Note that Base defines iterate(x::Number) = (x, nothing) and getindex(x::Number) = x (which is questionable, but I think outside the scope of this conversation), and I think it would basically be incorrect for a <:Number to define anything else.