Which of `::` and `where` should take precedence?

Currently (Julia v1.7), where takes precedence over ::. For example:

julia> :(f(x::T where T <: Integer))
:(f(x::(T where T <: Integer)))

However, this means that the return type annotation will not play nice with parametric methods:

julia> f(x::T)::Int where T<:Integer = x
ERROR: UndefVarError: T not defined

because the parametric definition got bundled with Int instead:

julia> :(f(x::T)::Int where T<:Integer)
:(f(x::T)::(Int where T <: Integer))

The workaround is to do either of:

julia> (f(x::T)::Int) where T<:Integer = x

julia> function (f(x::T)::Int) where T<:Integer
           return x
       end

which looks like code-smell…

As such, I would like to discuss the possibility of having :: take precedence over where instead. This would give us the following result:

julia> :(f(x::T where T <: Integer))
:(f((x::T) where T <: Integer))

julia> :(f(x::T)::Int where T<:Integer)
:((f(x::T)::Int) where T <: Integer)

and so the outer parametric definition correctly tag the function instead of the return type. This way we can go with the cleaner syntax of:

julia> f(x::T)::Int where T<:Integer = x

julia> function f(x::T)::Int where T<:Integer
           return x
       end

or if we so willed it:

julia> function rand_integer_type()::(T where T<:Integer)
           ...
       end

Not sure whether there’s any issue that arises due to this change, but since f(x::T) where T<:Integer = x works and:

julia> :(f(x::T) where T<:Integer)
:(f(x::T) where T <: Integer)

I would believe that the parametric information can reach f.

Would like to seek our opinion on this please :pray:

See JuliaLang/julia#21847.

3 Likes

Wow how did you reach the exact issue so fast >_< I hope I have better skill when it comes to searching the various Julia resources…

Notice that the second example already works as is. It’s only in “short form” definitions of functions where the issue arises.

Ah that’s true, thanks for pointing out. Indeed:

julia> :(function f(x::T)::Int where T<:Integer
                  return x
              end)

:(function (f(x::T)::Int) where T <: Integer
      #= REPL[1]:1 =#
      #= REPL[1]:2 =#
      return x
  end)

That looks like a hack to me though, i.e. I can’t determine the precedent just by looking at the local code of A::B where C…