# Unexpected type instability with getproperty()

**URL:** <https://discourse.julialang.org/t/unexpected-type-instability-with-getproperty/80381>\
**Category:** General Usage\
**Created:** [May 2, 2022, 6:04pm UTC](https://discourse.julialang.org/t/unexpected-type-instability-with-getproperty/80381 "2022-05-02T18:04:47Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![michakraus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/michakraus/32/222105_2.png) [@michakraus](https://discourse.julialang.org/u/michakraus)\
**Post date:** [May 2, 2022, 6:04pm UTC](https://discourse.julialang.org/t/unexpected-type-instability-with-getproperty/80381/1 "2022-05-02T18:04:47Z")

</div>

I have a data type that stores a vector `z` and a list of views to that vector, whose index ranges can be specified by the user, here `a` and `b`. The views are also accessible via `m.a` and `m.b` by overloading `getproperty`.

Depending on how and where I create those views `getproperty` is type stable or not. Please see the following MWE:

```julia
import Base.hasproperty, Base.getproperty

struct MyStruct{T, ZT <: AbstractVector{T}, VT <: NamedTuple}
    z::ZT
    v::VT

    # function MyStruct(z::ZT, vars) where {ZT}
    # # this is type-stable
    # v = ( a = view(z, 1:2), b = view(z, 3) )

    # # this is not type-stable
    # # v = NamedTuple{keys(vars)}(( view(z, idx_range) for idx_range in vars ))

    # new{eltype(z), ZT, typeof(v)}(z, v)
    # end
end

function MyStruct(z::ZT, vars) where {ZT}
    # this is type-stable
    # v = ( a = view(z, 1:2), b = view(z, 3) )

    # this is also type-stable
    v = NamedTuple{keys(vars)}(( view(z, idx_range) for idx_range in vars ))

    MyStruct{eltype(z), ZT, typeof(v)}(z, v)
end

function Base.getproperty(x::MyStruct{T, ZT, VT}, s::Symbol) where {T, ZT, VT}
    if hasfield(VT, s)
        return getfield(x, :v)[s]
    else
        return getfield(x, s)
    end
end

function test()
    m = MyStruct(rand(3), ( a = 1:2, b = 3 ))
    @time m.z
    @time m.a
    @time m.b
end

test();

```

If I have an inner constructor, creating the views statically leads to a type-stable `getproperty`, but creating them dynamically leads to an unstable `getproperty`. If I have an outer constructor, both lead to a type-stable `getproperty`.

I do not quite understand this behaviour. The type parameter `VT` of `MyStruct` has the same value in all examples.

I tested this with Julia v1.7.2 and 1.8.0-beta3 on macOS (M1).

---

<div class="post-metadata">

**Author:** ![jipolanco](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jipolanco/32/12129_2.png) [@jipolanco](https://discourse.julialang.org/u/jipolanco)\
**Post date:** [May 2, 2022, 7:14pm UTC](https://discourse.julialang.org/t/unexpected-type-instability-with-getproperty/80381/2 "2022-05-02T19:14:47Z")

</div>

I don’t really know why one constructor is type-stable and the other is not. However, note that you can alternatively use `map` to construct the NamedTuple `v`:

```julia
v = map(idx_range -> view(z, idx_range), vars)

```

which happens to make your inner constructor type-stable (in my tests at least). This returns a `NamedTuple` with the same names as `vars`, and with the values transformed by the mapping.

---

<div class="post-metadata">

**Author:** ![michakraus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/michakraus/32/222105_2.png) [@michakraus](https://discourse.julialang.org/u/michakraus)\
**Post date:** [May 2, 2022, 7:24pm UTC](https://discourse.julialang.org/t/unexpected-type-instability-with-getproperty/80381/3 "2022-05-02T19:24:15Z")

</div>

Thanks a lot! This indeed does the trick.

---

<div class="post-metadata">

**Author:** ![michakraus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/michakraus/32/222105_2.png) [@michakraus](https://discourse.julialang.org/u/michakraus)\
**Post date:** [May 2, 2022, 7:25pm UTC](https://discourse.julialang.org/t/unexpected-type-instability-with-getproperty/80381/4 "2022-05-02T19:25:09Z")

</div>

I came across yet another variation:

```julia
struct MyStruct{T, ZT <: AbstractVector{T}, VT <: NamedTuple}
    z::ZT
    v::VT
end

# this is type-stable
function MyStruct(z::ZT, vars) where {ZT}
    v = NamedTuple{keys(vars)}(( view(z, idx_range) for idx_range in vars ))
    MyStruct{eltype(z), ZT, typeof(v)}(z, v)
end

# this is not type-stable
function MyStruct(z::ZT; vars = NamedTuple) where {ZT}
    v = NamedTuple{keys(vars)}(( view(z, idx_range) for idx_range in vars ))
    MyStruct{eltype(z), ZT, typeof(v)}(z, v)
end

function Base.getproperty(x::MyStruct{T, ZT, VT}, s::Symbol) where {T, ZT, VT}
    if hasfield(VT, s)
        return getfield(x, :v)[s]
    else
        return getfield(x, s)
    end
end

function test()
    # no allocations
    m = MyStruct(rand(3), ( a = 1:2, b = 3 ))
    @time m.z
    @time m.a
    @time m.b

    # allocations
    m = MyStruct(rand(3); vars = ( a = 1:2, b = 3 ))
    @time m.z
    @time m.a
    @time m.b
end

```

Note that the two constructors are identical, except for how its parameters are passed (argument vs. keyword argument).  
It seems really strange that depending on how the object is constructed, its `getproperty` method is either type stable or not.

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [May 2, 2022, 8:14pm UTC](https://discourse.julialang.org/t/unexpected-type-instability-with-getproperty/80381/5 "2022-05-02T20:14:36Z")

</div>

Another recent thread had a problem related to type-stability and keyword arguments. I am currently thinking that there may be some problem with Julia current handling of these two characteristics in tandem.

---

<div class="post-metadata">

**Author:** ![michakraus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/michakraus/32/222105_2.png) [@michakraus](https://discourse.julialang.org/u/michakraus)\
**Post date:** [May 2, 2022, 8:17pm UTC](https://discourse.julialang.org/t/unexpected-type-instability-with-getproperty/80381/6 "2022-05-02T20:17:47Z")

</div>

Very good point. Indeed, if I put a function barrier in my test function, there are no allocations in either case:

```julia
function _test(m)
    @time m.z
    @time m.a
    @time m.b
end

function test()
    m = MyStruct(rand(3), ( a = 1:2, b = 3 ))
    _test(m)

    m = MyStruct(rand(3); vars = ( a = 1:2, b = 3 ))
    _test(m)
end

```
