Why doesn't this function compile

Can anyone tell me why this code doesn’t compile:

function f1( x::T ) where {T}
    function f2( y::U ) where {U <: AbstractVector{T}}
        return 0.0
    end
    return f2
end

I think you also need to parameterize the T inside:

julia> function f1( x::T ) where {T}
           function f2( y::U ) where {U <: AbstractVector{T}} where T
               return 0.0
           end
           return f2
       end
f1 (generic function with 1 method)
2 Likes

It depends on what @atteson was hoping to accomplish. I read the code as an attempt to force the element type of the AbstractVector y to be the same as the type of the argument passed into f1.

If that was the intent then adding the second where T won’t work since T gets bound to a different type in f2:

julia> function f1( x::T ) where {T}
                  function f2( y::U ) where {U <: AbstractVector{T}} where T
                                 return 0.0
                  end
                  return f2
              end
f1 (generic function with 1 method)

julia> a = f1(1.0)
(::var"#f2#12") (generic function with 1 method)

julia> a(["this","that"])
0.0 

This alternative forces the element type of y to be the same as the type of the argument to f1:

julia> function f1(x::T ) where {T}
           function f2(y::AbstractVector{T})
               return 0.0
           end
           return f2
       end
f1 (generic function with 1 method)

julia> a = f1(1.0)
(::var"#f2#11"{Float64}) (generic function with 1 method)

julia> a([1.0,2.0])
0.0

julia> a([1,2])
ERROR: MethodError: no method matching (::var"#f2#11"{Float64})(::Vector{Int64})
Closest candidates are:
  (::var"#f2#11"{T})(::AbstractVector{T}) where T at REPL[24]:2
Stacktrace:
 [1] top-level scope
   @ REPL[27]:clock130: 
3 Likes

Thank you. That is my intention. Let me change the problem to why the following doesn’t compile. In this case, doing what you suggest would, in my experience, lead to potential type instability since the code wouldn’t be compiled for each possible type of of AbstractVector{T}.

function f1( x::T ) where {T}
    function f2( y::AbstractVector{U} ) where {U <: AbstractVector{T}}
        return 0.0
    end
    return f2
end

What failed is the method definition, not compilation (which happens upon function call).

You should also know that in Julia, nested methods like f2 aren’t defined each time outer methods like f1 are called. They’re both defined at the same time, it’s more like the inner one is also implemented with a hidden struct is instantiated at each call of f1 to “capture” f1 variables, look up “functors” in the docs. However, captured variables must be in the method body, not the header type annotations, hence your original UndefVarError: T not defined for f2.

This however is perfectly legal and doesn’t need to be nested to capture any variables at all. Think of f3’s T as a variable that is inferred from the argument y at method call, and f3 is compiled for every type of T (and U).

julia> function f3( y::AbstractVector{U} ) where {T, U <: AbstractVector{T}}
         return T, U, typeof(y)
       end

where {T, U <: AbstractVector{T}} is equivalent to where {U <: AbstractVector{T}} where T, so like @brianguenter said, adding the extra where T to f2 actually does the same thing as f3, meaning the T for f2 is different from the T for f1.

1 Like