Use function argument inside optional (default) arguments type specification

Hi,

I encountered the following behaviour. I can use a function’s arguments to set the default values for optional (default) arguments:

julia> f(a::Int64; b::Int64 = a-1 ) = b
f (generic function with 1 method)

But I can’t use the same argument to specify the type of optional (default) arguments:

julia> g(a::Int64; b::NTuple{a, Int64} = Tuple(repeat([a], a))) = b
ERROR: UndefVarError: a not defined
Stacktrace:
 [1] top-level scope
   @ REPL[3]:1

Why is this? Is there any way to specify the optional (default) argument types of a function using the function’s arguments?

It’s not quite as convenient, but you can use nothing as the default and check for it:

julia> function g(a; b=nothing)
         if b === nothing
           b = ntuple(_ -> a, a)
         end
         b
       end
g (generic function with 1 method)

julia> g(2)
(2, 2)

julia> g(2, b=(3, 4))
(3, 4)

The something function can also make this easier:

julia> g(a; b=nothing) = something(b, ntuple(_ -> a, a))
g (generic function with 1 method)

julia> g(2)
(2, 2)

julia> g(2, b=(3, 4))
(3, 4)
help?> something
search: something

  something(x, y...)

  Return the first value in the arguments which is not equal to nothing, if any. Otherwise throw an error.
  Arguments of type Some are unwrapped.

The reason this doesn’t work is that a is a value that is not known until runtime.
Apparently, you cannot depend a type signature (of b) on the runtime value of a.

The following would work:

julia> function g(::Val{a}, b::NTuple{a, Int64} = Tuple(repeat([a], a))) where a
           return b
       end
g (generic function with 2 methods)

julia> g(Val(2))
(2, 2)


julia> g(Val(3))
(3, 3, 3)

Of course, you can also do:

julia> function g2(a)
           Tuple(repeat([a], a))
       end
g2 (generic function with 1 method)

julia> g2(2)
(2, 2)

julia> g2(3)
(3, 3, 3)

julia> @code_warntype g2(3)
Variables
  #self#::Core.Const(g2)
  a::Int64

Body::Tuple{Vararg{Int64, N} where N}
1 ─ %1 = Base.vect(a)::Vector{Int64}
│   %2 = Main.repeat(%1, a)::Vector{Int64}
│   %3 = Main.Tuple(%2)::Tuple{Vararg{Int64, N} where N}
└──      return %3

As @code_warntype displays, the function is type instable since the output type depends on the value of a. Such a pattern should be avoided in performance critical code.