Ways of using constructor of a parametric type

I noticed a strange behavior when using constructors of parametric types.

If a constructor does not have constraints on type parameters, then both type inference and providing explicit type argument work:

struct Foo{T}
  val :: T
  Foo(x :: T) where T = new{T}(x)
end

julia> Foo(3)
Foo{Int64}(3)

julia> Foo{Number}(3)
Foo{Number}(3)

But if I add a constraint on a type parameter, then depending on the syntax of a constructor only one of the constructor’s calls works:

struct Bar{T}
  val :: T
  Bar(x :: T) where T <: Number = new{T}(x)
end

struct Baz{T}
  val :: T
  Baz{T}(x :: T) where T <: Number = new{T}(x)
end


julia> Bar(2)
Bar{Int64}(2)

julia> Bar{Number}(2)
ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type Bar{Number}
This may have arisen from a call to the constructor Bar{Number}(...),
since type constructors fall back to convert methods.
Stacktrace:
 [1] Bar{Number}(::Int64) at ./sysimg.jl:102

julia> Baz{Number}(2)
Baz{Number}(2)

julia> Baz(2)
ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type Baz
This may have arisen from a call to the constructor Baz(...),
since type constructors fall back to convert methods.
Stacktrace:
 [1] Baz(::Int64) at ./sysimg.jl:102

Is this a bug?

1 Like

Nope, this is intended behavior. You are defining Bar(...) and Baz{T}(...) where T functions (which happen to be constructors), but not the other ones you are trying to call. See

Also, Foo{Number}(3) does not work with your code in 0.6. Could it be the result of earlier experimentation?

2 Likes

Thank you for reply!

This is 0.7.0.

1 Like