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.
1 Like

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.

2 Likes