Unlike T, N isn’t constrained by any type in the signature of your method, so it pretty much doesn’t exist: how can the assertion on the return type make sure you’re returning a NTuple{N, T} if N comes from nowhere?
@giordano 's answer is correct, but I didn’t understand it in the first reading. I think it’s clearer to mention that the compiler should be able to infer what type is Tthrough the input x, but there is no input that clarifies what type should N be.
julia> function test(x::Vector{T}, y) where T
return (1,2,3)::NTuple{N,T} where N
end
test (generic function with 1 method)
julia> test(Int[], ())
(1, 2, 3)
Okay, these seem to be the droids you’re looking for:
julia> function test(x::Vector{T}, y)::(NTuple{N,T} where N) where T
return (1,2,3)
end
test (generic function with 1 method)
julia> test(Int[], ())
(1, 2, 3)
Unlike the previous code, which simply performs type-assertion, this one does type-conversion.
Hm, I don’t know that the warning has the right choice of wording.
julia> foo() where N = N
WARNING: method definition for foo at REPL[1]:1 declares type variable N but does not use it.
foo (generic function with 1 method)
julia> foo()
ERROR: UndefVarError: `N` not defined
seems like it would be better if the warning said something along the lines of
Thank you all for the replies. The first response did answer why, but I realize I should have also asked “and what can I do about it?” I can’t just omit N, because NTuple requires something in that slot. I need a way to specify the type: “an NTuple of unknown length and elements of type T”. NTuple{_,T} doesn’t work. NTuple{T} doesn’t work.
It appears @uniment answered that with (NTuple{N,T} where N) which makes sense. I wasn’t familiar with using parentheses and multiple where clauses like that, so I’ll keep that in mind.
I don’t think there is any difference. If you look at the generated code, I think it always generates a call to convert when there is a return type specified.
julia> function bar(::Type{T}, x)::T where T; x end
function baz(::Type{T}, x) where T; x::T end
baz (generic function with 1 method)
julia> bar(Float64, 1)
1.0
julia> baz(Float64, 1)
ERROR: TypeError: in typeassert, expected Float64, got a value of type Int64
The error here is coming from the x::T, not the return of the method. Change it to: function baz(::Type{T}, x) where T; x end
and it won’t give an error anymore.
Although this works, this is (strictly speaking) an abuse of the type system: leveraging an undocumented implementation detail that <:Any currently simply doesn’t perform any typecheck on type parameters (after all, 3<:Any throws an error, so it’s nonsensicle that NTuple{3,Int}<:NTuple{<:Any,Int} returns true). As such, when this loophole eventually gets fixed, your code will break.
It appears I mistook your intent then: I thought you intended to communicate that the element type of the function’s output tuple is constrained in accordance with the argument’s element type. If not, you could’ve always just written this:
function test(x::Vector, y)::NTuple
return (1,2,3)
end