Outer constructor for Base.@kwdef with constrained parametric type

julia> Base.@kwdef struct A1{T <: Real}
           n::Int
       end

julia> A1{T}() where {T} = A1{T}(n=3)

julia> Base.@kwdef struct A2{T}
           n::Int
       end

julia> A2{T}() where {T} = A2{T}(n=3)

julia> A1{Float64}()
ERROR: UndefKeywordError: keyword argument n not assigned
Stacktrace:
 [1] A1{Float64}()
   @ Main ./util.jl:462
 [2] top-level scope
   @ REPL[6]:1

julia> A2{Float64}()
A2{Float64}(3)

The only difference between A1 and A2 is that the constraint T <: Real on the type parameter.
Why does A1{Float64}() fail, while `A2{Float64}() does not?

julia> versioninfo()
Julia Version 1.6.1
Commit 6aaedecc44 (2021-04-23 05:59 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-11.0.1 (ORCJIT, haswell)
Environment:
  JULIA = /home/jmlim/appl/julia-1.6.1/bin/julia
  JULIA_EDITOR = "/home/jmlim/.vscode-server/bin/c3f126316369cd610563c75b1b1725e0679adfb3/node"
  JULIA_NUM_THREADS = 1

I can’t find out the “why” but it works with:

A1{T}() where {T  <: Real } = A1{T}(n=3)
1 Like

Using @kwdef already defines a method for A1{T}() where T<:Real, which requires a keyword, and which is called even when you define a (somewhat less specific) method A1{T}() = ...:

julia> Base.@kwdef struct A1{T <: Real}
           n::Int
       end

julia> methods(A1{Float})
# 2 methods for type constructor:
[1] A1{T}(; n) where T<:Real in Main at util.jl:483
[2] A1{T}(n) where T<:Real in Main at REPL[1]:2

(remember that keywords are not dispatched on).

2 Likes