Dynamic type declarations


#1

Hello, I just stumbled across

local f = function(T::Type)
return function(x::T)
x + 1
end
end

which gives “UndefVarError: T not defined” due to the inner x::T.

This is a bit surprising to me, as using dynamic types is allowed for local, i.e. the following is ok:

local f = function(T::Type)
local x::T = 3
x + 1
end

Now, I’m aware that I could rewrite the first example with something like eval(Expr(:function, Expr(:tuple, Expr(:(::), :x, T) ..., but unfortunately, in my specific use case I actually need a capture (thus breaking the eval approach due to eval’s global scope), i.e. what I actually want to achieve is this:

local y
local f = function(T::Type)
return function(x::T)
x + y
end
end

Is there any sensible to achieve this?


#2

One option would be to replace function(x::T) with

function(x)
    typeassert(x, T)
    x + y
end

#3

I know, I used x::T + y temporarily, which is similar, I guess.

But I wonder if needing to have the parameter types of lambdas fixed in this way is expected behaviour in Julia or if it can be considered a bug.

Yet using typeassert seems to be a very good solution, as it produces efficient code:

function f(x)
  typeassert(x, Int)
  x + 1
end
@code_llvm f(10)
define i64 @julia_f_71777(i64) #0 {
top:
  %1 = add i64 %0, 1
  ret i64 %1
}

#4

It is expected. The closure you create on multiple invocations of the parent function are of the same type and the method definition including the dispatch specification is statically computed at compile time. The sementic is defined this way since doing it otherwise would be very expensive, either you have to create new type or adding new method to a type at runtime.

That said, it is not impossible to transform the code to do an assertion in this case automatically. However, since it will NOT have the same semantic as specifying the dispatch it’s a little bit confusing (not much) if we do so.


#5

Note that the typeassert isn’t necessary if you’re just trying to produce efficient code:

julia> function f(x)
       x + 1
       end
f (generic function with 1 method)

julia> @code_llvm f(10)

define i64 @julia_f_70822(i64) #0 {
top:
  %1 = add i64 %0, 1
  ret i64 %1
}