Is there a built-in function to get the length of a tuple as a Val{N}?
Basically I’m looking for something that would be of the form:
tuplen(::Type{Tuple{}}) = Val{0}()
tuplen(::Type{Tuple{T}}) where T = Val{1}()
tuplen(::Type{Tuple{T1, T2}}) where {T1, T2} = Val{2}()
tuplen(::Type{Tuple{T1, T2, T3}}) where {T1, T2, T3} = Val{3}()
...
In case this is an XY problem - what I’m actually trying to do is convert an array-of-tuples representation to a tuple-of-arrays representation, and with the above definition I can do:
"""
Convert an Array-of-Tuples to a Tuple-of-Arrays.
"""
toa(aot) = ntuple(i->getindex.(aot, i), tuplen(eltype(aot)))
without the above I could use length(first(aot)) to get the length but that assumes the array is not empty.
edit: as a nested XY problem, I just realized that Plots can take vectors of tuples natively and I don’t need to transform my data to tuple-of-vectors after all. Carry on…
It’s also worth noting that the compiler is pretty good at dealing with tuple lengths as regular old integers using the built-in length function. For example, we can write a function that looks like it might be type-unstable:
julia> function maybe_type_unstable(x::Tuple)
if length(x) == 2
return 1.0
else
return "hello world"
end
end
maybe_type_unstable (generic function with 1 method)
but the compiler is smart enough to figure out the value of length(x) at compile time and infer the result:
Yep this function gets the length of a tuple, not from the tuple type ad was requested at the beginning (and as it seems the OP is referenced) . Actually I found a solution asking another question there How can i index a tuple type? so nvm
The solution in the other thread may be efficient, but is probably an implementation detail that cannot be relied upon.
As it is said in the accepted answer:
So you can use NTuple{N, T} to match any Tuple{...}. The only matter is that you want the function to take tuple types instead of tuples. The solution below is correct and do not rely in any implementation details, while it may be ugly and inefficient.
julia> tuple_type_length(x) = (n = -1; while !(x <: NTuple{n+=1, Any}); end; return n)
tuple_type_length (generic function with 1 method)
julia> tuple_type_length(Tuple{Int, Char})
2
julia> tuple_type_length(Tuple{Int, Char, String})
3
Any concrete type should be fine with fieldtype. Even many incomplete types with defined-but-type-unspecified fields such as Complex{T} where T, Tuple{T,T} where T, and Tuple{Tuple{Vararg}} will work.
Examples of types that will fail are Tuple (with no further elaboration), Tuple{Vararg}, and NTuple{N,Any} where N. The number of fields that these types have is undefined.
So fieldcount should work with almost any type that actually has a number of fields. One might be able to cook up some pathological corner cases, but I haven’t thought of one yet. If you find one that fails that you think shouldn’t, you can consider opening a bug report.
I believe there is no problem, but I would adopt the simpler fieldcount solution now that I am aware of it, unless you want the code to fail in non-tuple types.
OK, but fieldcount gives a plain integer result, as opposed to the initial requirement that the result be a Val-wrapped value.
I agree that, since constant propagation is likely to work well in many cases, fieldcount would probably a very good and standard way to do this (but in those cases, the question then becomes: why not simply length?)
length works if you have an instance of the object, true, but I thought the idea was to have a function that would work directly over the type?
Also, yes, fieldtype does not return a Val but neither does N in the parameter type annotation, both give Int values which can after be wrapped inside Val. It may be that one solution is recognized as type-stable by the compiler (i.e., recognizes the static mapping between input and output types) and the other solution isn’t, but this is speculation and should be checked.