I have a parametric type that I’m trying to specialize getproperty
for. There are some properties which I’d like to always access independently of the type parameter, and there are some that only apply to specific type parameters. I’d like to factor out the type parameter-independent implementation of getproperty
to avoid code duplication, but I can’t seem to do so without losing type stability.
Here is a MWE:
struct Foo{B, T, ABC}
bar::B
xy::T
abc::ABC
end
# this is used as a parameter in `Foo`
struct Bar{U}
u::U
end
# shared `getproperty` code independent of the `bar` parameter
function _getproperty(x::Foo, s::Symbol)
if s==:x
return getfield(x, :xy)[1]
elseif s==:a
return getfield(x, :abc)[1]
elseif s==:b
return getfield(x, :abc)[2]
else
return getfield(x, s)
end
end
# `bar`-specific (and type unstable) `getproperty` code
function Base.getproperty(x::Foo{<:Bar}, s::Symbol)
if s==:u
return length(getfield(x.bar, :u))
else
_getproperty(x, s)
end
end
using Test
function f(x::Foo)
x.x, x.xy, x.a, x.u
end
bar = Bar(1)
@inferred f(Foo(bar, (1., 2), "ab"))
The @inferred
test fails with ERROR: return type Tuple{Float64, Tuple{Float64, Int64}, Char, Int64} does not match inferred return type NTuple{4, Any}
.
The following code is inferrable but doesn’t factor the bar
-independent code out and is harder to extend.
# this is type stable but harder to extend
function Base.getproperty(x::Foo{<:Bar}, s::Symbol)
if s==:u
return length(getfield(x.bar, :u))
elseif s==:x
return getfield(x, :xy)[1]
elseif s==:a
return getfield(x, :abc)[1]
elseif s==:b
return getfield(x, :abc)[2]
else
return getfield(x, s)
end
end
Is there a good way to factor out the “shared” getproperty
code without losing type stability here?