Typing with "where" statements leads to UndefVarError error?

Hi all. I’m very new to Julia. Using 1.2.0 in these examples.

I hit upon a little snag when i wanted to partially specify the return types for a function. E.g, specifying that the output will be SVector{N,Int} for any N , where (unlike arrays) the type was given as the second type parameter.
However, the typical where approach doesn’t seem to work here:

function f()::SVector{N,Int} where {N}
    return SA[1,2,3]
end
julia> f()
UndefVarError: N not defined

Stacktrace:
 [1] f() at ./In[27]:2
 [2] top-level scope at In[27]:6

though, strangely, the one-liner version works;

g()::SVector{N,Int} where {N} = SA[1,2,3]
julia> g()
3-element SArray{Tuple{3},Int64,1,3} with indices SOneTo(3):
 1
 2
 3

Now, even stranger (to me) is that this works:

function f(x::SVector{N,Int})::SVector{N,Int} where {N}
    return 2 .* x
end
julia> f(SA[1,2,3])
3-element SArray{Tuple{3},Int64,1,3} with indices SOneTo(3):
 2
 4
 6

whereas the one-liner equivalent now doesn’t work instead:

g(x::SVector{N,Int})::SVector{N,Int} where {N} = 2 .* x
UndefVarError: N not defined

Stacktrace:
 [1] top-level scope at In[18]:1

in fact, it can’t even be defined unlike the original f().

In my particular usecase, I can probably just drop the return type info for my generic functions anyway, but I thought i had i grasp on the typesystem until I hit upon this.
Is this a bug or shortcoming, or am I simply doing it wrong?
Thanks

1 Like

In general, you shouldn’t specify return types. In almost all cases, there is no benefit for doing so.

3 Likes

On rare occasion it is useful, so it would still be good to get an answer.

You are seeing the difference between f()::(SVector{N,Int} where {N}) and (f()::SVector{N,Int}) where {N}.

For f()::(SVector{N,Int} where {N}), SVector{N,Int} where {N} is a well defined (abstract) type so there’s no error.

For (f()::SVector{N,Int}) where {N}, N is a parameter of the function that is used in the return type declaration so it must be deducted from the argument for it to work. You get an error in that case since N cannot be deducted.

Finally, the two function definition syntax behaves slightly differently in this case, and that’s why you only get error in one and not the other. You can see the difference by dumping the expression or use Meta.show_sexpr or any number of other tools that doesn’t try to be smart about adding parenthesis during printing. Adding appropriate parenthesis should fix the issue.


And for completeness

(f(x::SVector{N,Int})::SVector{N,Int}) where {N} works though the return type is a concrete type of SVector{N,Int} with N taking a specific value rather than an abstract type.

and

g(x::SVector{N,Int})::(SVector{N,Int} where {N}) cannot be defined since you are using a SVector{N,Int} as argument type but the N is not defined anywhere.

6 Likes

Thanks yuyichao!

So, if I wanted to specify an SVector integer input of length N and output of any length M, i would have to do

function f(x::SVector{N,Int})::(SVector{M,Int} where M) where N
    return SA[1,2,3,4]
end
f(SA[1,2,3])

but I do see the advantage of this, as N becomes a variable for the output type

function double_up(x::SVector{N,Int})::SVector{2*N,Int} where N
    return vcat(x, x)
end

(In my particular usecase, I think I will just be return types completely)

Best regards, Mikael