It’s not a big deal, but I’d kinda like to hide a type parameter of a struct. The value of this parameter is determined from the value of the other parameters.
Here’s a small example including working and unexpectedly broken tests. I’m using isbitstype(T)
to decide what the second type parameter is and an unsuccessful attempt to hide the type parameter that has behaviour that I find strange (the constructor works for some types and throws an exception for others. I would expect it to work for all reasonable types, or for none, or to throw a parse error).
# No attempt at hiding the type parameter
struct S1{T, U}
v::Ref{U}
function S1{T}() where {T}
U = isbitstype(T) ? T : Union{Some{T}, Nothing}
new{T, U}(Ref{U}())
end
end
# Nice, but let's try to hide the type `U` so as not to leak
# implementation details
struct S2{T}
v::Ref{isbitstype(T) ? T : Union{Some{T}, Nothing}}
function S2{T}() where {T}
U = isbitstype(T) ? T : Union{Some{T}, Nothing}
new{T}(Ref{U}())
end
end
using Test
@testset begin
# Works as expected
@test S1{String}().v isa Ref{Union{Some{String}, Nothing}}
@test S1{Int}().v isa Ref{Int}
# Works
@test S2{String}().v isa Ref{Union{Some{String}, Nothing}}
# Constructor throws an error when isbitstype(T)
# I'd expect it to either work, be broken for all types, or be some kind of parse error.
@test_broken S2{Int}().v isa Ref{Int}
end
# Run this to get the exception below:
S2{Int}()
#=
Exception is from Julia 1.9.2, but an almost identical error also occurs on a
recent build of Julia 1.11 (master)
ERROR: LoadError: MethodError: Cannot `convert` an object of type Base.RefValue{Int64} to an object of type Some{Int64}
Closest candidates are:
convert(::Type{Some{T}}, !Matched::Some{T}) where T
@ Base some.jl:37
convert(::Type{Some{T}}, !Matched::Some) where T
@ Base some.jl:38
convert(::Type{T}, !Matched::T) where T
@ Base Base.jl:64
...
Stacktrace:
[1] convert(#unused#::Type{Union{Nothing, Some{Int64}}}, x::Base.RefValue{Int64})
@ Base ./some.jl:36
[2] Base.RefValue{Union{Nothing, Some{Int64}}}(x::Base.RefValue{Int64})
@ Base ./refvalue.jl:8
[3] convert(#unused#::Type{Ref{Union{Nothing, Some{Int64}}}}, x::Base.RefValue{Int64})
@ Base ./refpointer.jl:104
[4] S2{Int64}()
@ Main ~/example.jl:24
[5] top-level scope
@ ~/example.jl:43
in expression starting at /home/colin/example.jl:43
=#