# Computed parametric field types using assertions in \`getproperty\`

**URL:** <https://discourse.julialang.org/t/computed-parametric-field-types-using-assertions-in-getproperty/43865>\
**Category:** General Usage\
**Tags:** parametric-types\
**Created:** [July 29, 2020, 10:16am UTC](https://discourse.julialang.org/t/computed-parametric-field-types-using-assertions-in-getproperty/43865 "2020-07-29T10:16:04Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![mhauru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhauru/32/208880_2.png) [@mhauru](https://discourse.julialang.org/u/mhauru)\
**Post date:** [July 29, 2020, 10:16am UTC](https://discourse.julialang.org/t/computed-parametric-field-types-using-assertions-in-getproperty/43865/1 "2020-07-29T10:16:04Z")

</div>

Quite often I have parametric types where the field types depend in some simple but non-trivial way on the type parameters. Something like

```julia
struct Dada{N}
    a::Array{Float64, N}
    b::Array{Float64, N+1}
end

```

The above isn’t valid Julia, and I’ve understood that the recommended way to deal with such cases, assuming one wants to maintain type stability, is

```julia
struct DadaImp1{N, Ta, Tb}
    a::Ta
    b::Tb

    function DadaImp1{N}(a::Array{Float64}, b::Array{Float64}) where {N}
        Ta = Array{Float64, N}
        Tb = Array{Float64, N+1}
        a::Ta
        b::Tb
        return new{N, Ta, Tb}(a, b)
    end
end

```

Now I’ve recently been using this pattern when building a [library](https://github.com/mhauru/MERA.jl), which in turn uses types from another [library](https://github.com/Jutho/TensorKit.jl) that makes extensive use of this pattern, and I’m starting to end up with pretty ridiculous types such as

```julia
ModifiedBinaryLayer{TensorMap{ℤ₂Space,2,2,ℤ₂,TensorKit.SortedVectorDict{ℤ₂,Array{Complex{Float64},2}},FusionTree{ℤ₂,2,0,1,Nothing},FusionTree{ℤ₂,2,0,1,Nothing}},TensorMap{ℤ₂Space,2,1,ℤ₂,TensorKit.SortedVectorDict{ℤ₂,Array{Complex{Float64},2}},FusionTree{ℤ₂,2,0,1,Nothing},FusionTree{ℤ₂,1,0,0,Nothing}},TensorMap{ℤ₂Space,2,1,ℤ₂,TensorKit.SortedVectorDict{ℤ₂,Array{Complex{Float64},2}},FusionTree{ℤ₂,2,0,1,Nothing},FusionTree{ℤ₂,1,0,0,Nothing}}}

```

Most of that information is entirely redundant, the non-trivial part would simply be `ModifiedBinaryLayer{ℤ₂Space, Array{Complex{Float64}}}`.

I don’t know if these ballooning type parametrizations affect inference (please enlighten me if you can), but they definitely affect my sanity when reading error messages, `@code_warntype`, and various other things. Hence, I’ve been thinking of doing instead something like this:

```julia
struct DadaImp2{N}
    a::Array{Float64}
    b::Array{Float64}

    function DadaImp2{N}(a::Array{Float64}, b::Array{Float64}) where {N}
        Ta = Array{Float64, N}
        Tb = Array{Float64, N+1}
        a::Ta
        b::Tb
        return new{N}(a, b)
    end
end

function Base.getproperty(d2::DadaImp2{N}, s::Symbol) where {N}
    if s === :a
        T = Array{Float64, N}
    elseif s === :b
        T = Array{Float64, N+1}
    else
        T = Any
    end
    return getfield(d2, s)::T
end

```

(For most purposes?) type stability is still maintained, since the compiler can figure out  
what type `d2.b` should be.

Assuming my fields aren’t bits types, but pointers, are there any downsides to this way of doing things?

PS. The optimal solution would be for this to get implemented: [https://github.com/JuliaLang/julia/issues/18466](https://github.com/JuliaLang/julia/issues/18466) No idea if that’s in the cards though.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [July 29, 2020, 11:06am UTC](https://discourse.julialang.org/t/computed-parametric-field-types-using-assertions-in-getproperty/43865/2 "2020-07-29T11:06:15Z")

</div>

> [@mhauru](#):
>
> pretty ridiculous types

I am not sure what that means in this context, but I admit that I derive most of my amusement from books, cartoons, and webcomics, not Julia types.

> [@mhauru](#):
>
> I don’t know if these ballooning type parametrizations affect inference (please enlighten me if you can)

It is pretty orthogonal to inference and execution speed: you need concrete types in most cases for the latter, while (seemingly) redundant type parametrizations are required to enforce constraints on types other than subtype relations.

If it bothers you, you may want to define a custom `show` method.

> [@mhauru](#):
>
> I’ve been thinking of doing instead something like this […]
> 
> ```julia
> a::Array{Float64}
> b::Array{Float64}
> 
> ```

These are not concrete, so performance may suffer if constant folding fails in the `getproperty`. Also, I think there are much simpler and cleaner solutions.

In case you don’t need the type array dimensions in the type and want to allow other array types, you could do your original example as

```julia
struct DadaImp3{TA<:AbstractArray,TB<:AbstractArray}
    a::TA
    b::TB
    function DadaImp3(a::AbstractArray{T,N}, b::AbstractArray{S,M}) where {T,S,N,M}
        @assert M == N + 1
        return new{typeof(a),typeof(b)}(a, b)
    end
end

```

If you do want to restrict to `Array{Float64}`, you can use something like

```julia
struct DadaImp4{N,M}
    a::Array{Float64,N}
    b::Array{Float64,M}
    function DadaImp4(a::Array{Float64,N}, b::Array{Float64,M}) where {N,M}
        @assert M == N + 1
        return new{N,M}(a, b)
    end
end

```

Or do an interim case for a common element type with `DadaImp{T,N,M}` etc. It all depends on your use case.

---

<div class="post-metadata">

**Author:** ![mhauru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhauru/32/208880_2.png) [@mhauru](https://discourse.julialang.org/u/mhauru)\
**Post date:** [July 29, 2020, 12:21pm UTC](https://discourse.julialang.org/t/computed-parametric-field-types-using-assertions-in-getproperty/43865/3 "2020-07-29T12:21:26Z")

</div>

> [@Tamas\_Papp](#):
>
> It all depends on your use case.

The `Array{Float64, N+1}` is just a mock-up, the actual use case would be more like

```julia
struct Dada{N,T,G}
    a::f(N, T, G)
    b::g(N, T, G)
end

```

where `f` and `g` would be some type stable (and `@pure`?) functions.

> [@Tamas\_Papp](#):
>
> These are not concrete, so performance may suffer if constant folding fails in the `getproperty` .

My point is that if the return type of `getproperty` can be inferred and the field is stored as a pointer anyway, as far as I can see non-concreteness shouldn’t matter. What you are saying about constant folding seems key. If I do `@code_warntype (x -> x.b)(d2)` to confirm that the type of `d2.b` is correctly inferred, are there still other situations where constant folding would fail, and thus abstract types would pop up in inference? I know nothing about how constant folding works.

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [July 29, 2020, 12:54pm UTC](https://discourse.julialang.org/t/computed-parametric-field-types-using-assertions-in-getproperty/43865/4 "2020-07-29T12:54:34Z")

</div>

Don’t use `@pure` It doesn’t mean what you think it means and will make you sad.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [July 29, 2020, 1:26pm UTC](https://discourse.julialang.org/t/computed-parametric-field-types-using-assertions-in-getproperty/43865/5 "2020-07-29T13:26:44Z")

</div>

> [@mhauru](#):
>
> the field is stored as a pointer anyway

That’s an implementation detail you can never be sure about — it is up to the compiler.

> [@mhauru](#):
>
> I know nothing about how constant folding works.

It has some limitations but I think that in your case it would work. But why rely on it when it is not needed?

---

<div class="post-metadata">

**Author:** ![mhauru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhauru/32/208880_2.png) [@mhauru](https://discourse.julialang.org/u/mhauru)\
**Post date:** [July 29, 2020, 2:06pm UTC](https://discourse.julialang.org/t/computed-parametric-field-types-using-assertions-in-getproperty/43865/6 "2020-07-29T14:06:21Z")

</div>

> [@Tamas\_Papp](#):
>
> It has some limitations but I think that in your case it would work. But why rely on it when it is not needed?

I would prefer having type parametrisation match the way a human naturally thinks about the code. For instance, when writing a library, types are often a very user-facing part, and a notion of “object of type X parametrised by Y” may be very intuitive. Preferably how Y relates to the types of fields would be an implementation detail that a user wouldn’t have to care about, or be confronted with.

Taking the example from my opening post, `ModifiedBinaryLayer{ℤ₂Space, Array{Complex{Float64}}}` is a very natural type to have for my library: For anyone who knows what the code is about, it makes intuitive sense and its meaning is immediately clear. Having that expanded into something like

```julia
ModifiedBinaryLayer{TensorMap{ℤ₂Space,2,2,ℤ₂,TensorKit.SortedVectorDict{ℤ₂,Array{Complex{Float64},2}},FusionTree{ℤ₂,2,0,1,Nothing},FusionTree{ℤ₂,2,0,1,Nothing}},TensorMap{ℤ₂Space,2,1,ℤ₂,TensorKit.SortedVectorDict{ℤ₂,Array{Complex{Float64},2}},FusionTree{ℤ₂,2,0,1,Nothing},FusionTree{ℤ₂,1,0,0,Nothing}},TensorMap{ℤ₂Space,2,1,ℤ₂,TensorKit.SortedVectorDict{ℤ₂,Array{Complex{Float64},2}},FusionTree{ℤ₂,2,0,1,Nothing},FusionTree{ℤ₂,1,0,0,Nothing}}}

```

makes the code less readable and less self-documenting. It also makes it more annoying to write anything that explicitly refers to this type, such as method signatures and cases where this would be the field type for some other type.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [July 29, 2020, 3:30pm UTC](https://discourse.julialang.org/t/computed-parametric-field-types-using-assertions-in-getproperty/43865/7 "2020-07-29T15:30:14Z")

</div>

> [@mhauru](#):
>
> would prefer having type parametrisation match the way a human naturally thinks about the code

This is a very common request, but that’s not how Julia’s type parameters work. Again, you can use some subtype & triangular restrictions, but that’s pretty much it. The rest is enforced by the constructor, not the type system.

> [@mhauru](#):
>
> types are often a very user-facing part, and a notion of “object of type X parametrised by Y” may be very intuitive. Preferably how Y relates to the types of fields would be an implementation detail that a user wouldn’t have to care about, or be confronted with.

Just change the user facing API then — provide constructors that calculate implied fields, change methods for `show`, etc. You can hide pretty much everything from the “user”.

---

<div class="post-metadata">

**Author:** ![mhauru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhauru/32/208880_2.png) [@mhauru](https://discourse.julialang.org/u/mhauru)\
**Post date:** [July 29, 2020, 4:08pm UTC](https://discourse.julialang.org/t/computed-parametric-field-types-using-assertions-in-getproperty/43865/8 "2020-07-29T16:08:12Z")

</div>

> [@Tamas\_Papp](#):
>
> This is a very common request, but that’s not how Julia’s type parameters work. Again, you can use some subtype & triangular restrictions, but that’s pretty much it. The rest is enforced by the constructor, not the type system.

Well, surely noone would object to greater intuitiveness if it comes with no cost, and it seems to me that I can make the Julia type system work the way I want with some type assertions in `getproperty`. You clearly think that my proposal has downsides to it, but could you help me understand what they are? You mention “limitations” of constant folding, but I don’t know what you mean. Do you think that even if constant folding saves type stability in my example case, it might fail to do so in some more complicated situation?

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [July 30, 2020, 9:10am UTC](https://discourse.julialang.org/t/computed-parametric-field-types-using-assertions-in-getproperty/43865/9 "2020-07-30T09:10:28Z")

</div>

> [@mhauru](#):
>
> You mention “limitations” of constant folding, but I don’t know what you mean.

The semantics of constant propagation are not clearly defined in Julia; in theory the compiler can change its heuristics any time. That said, a simple `.` access that goes to `getproperty` should keep working.

My aversion to solutions like this is mainly a matter of taste: repeating all those types violates DRY, and it may be brittle if you want to extend this to cases which would otherwise be nice bits types (eg if you switch to static arrays). But if it works for you, that should be fine.

---

<div class="post-metadata">

**Author:** ![mhauru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhauru/32/208880_2.png) [@mhauru](https://discourse.julialang.org/u/mhauru)\
**Post date:** [August 12, 2020, 11:15am UTC](https://discourse.julialang.org/t/computed-parametric-field-types-using-assertions-in-getproperty/43865/10 "2020-08-12T11:15:23Z")

</div>

> [@Tamas\_Papp](#):
>
> you may want to define a custom `show` method.

I’ve been thinking about and trying various approaches to the question I was asking in this thread, including using what Tamas was proposing above, i.e. defining methods for `show` to hide some of the complexity of my parametric types. I just ran into some issues on GitHub that discuss problems that may arise from this, see

> <https://github.com/JuliaLang/julia/issues/22363#issuecomment-308472359>
>
> Here's the minimum repro
> \`\`\`julia
> \_ \_ \_(\_)\_ | A fresh approach t…o technical computing
> (\_) | (\_) (\_) | Documentation: https://docs.julialang.org
> \_ \_ \_| |\_ \_\_ \_ | Type "?help" for help.
> | | | | | | |/ \_\` | |
> | | |\_| | | | (\_| | | Version 0.7.0-DEV.511 (2017-06-08 21:22 UTC)
> \_/ |\\\_\_'\_|\_|\_|\\\_\_'\_| | (HEAD detached at origin/jb/namedtuples)/e2c2724982\* (fork: 6 commits, 5 days)
> |\_\_/ | x86\_64-apple-darwin16.6.0
> 
> julia\> struct Null end
> 
> julia\> Base.show(io::IO, ::Type{Union{T, Null}}) where {T} = print(io, "?$T")
> 
> julia\> struct WeakRefString{T} \<: AbstractString
> ptr::Ptr{T}
> len::Int # of code units
> ind::Int # used to keep track of a string data index
> end
> 
> julia\> Base.show(io::IO, ::Type{WeakRefString{T}}) where {T} = print(io, "WeakRefString{$T}")
> 
> julia\> temp = \[\]
> Segmentation fault: 11
> jq-mbp:julia jacobquinn$
> \`\`\`
> Note the last thing that happens is it's trying to display \`0-element Array{Any,1}\`.

both Jeff’s comment, and the issue and it’s referencing issues more generally.

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [August 14, 2020, 3:52am UTC](https://discourse.julialang.org/t/computed-parametric-field-types-using-assertions-in-getproperty/43865/11 "2020-08-14T03:52:20Z")

</div>

Have you tried this?

[https://github.com/vtjnash/ComputedFieldTypes.jl](https://github.com/vtjnash/ComputedFieldTypes.jl)

---

<div class="post-metadata">

**Author:** ![mhauru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhauru/32/208880_2.png) [@mhauru](https://discourse.julialang.org/u/mhauru)\
**Post date:** [August 14, 2020, 8:09am UTC](https://discourse.julialang.org/t/computed-parametric-field-types-using-assertions-in-getproperty/43865/12 "2020-08-14T08:09:07Z")

</div>

I have not, thanks for the tip. So does it just turn a thing like

```julia
@computed struct A{V <: AbstractVector}
    a::eltype(V)
end

```

into

```julia
struct A{V <: AbstractVector, E}
    a::E
    function A{V}(a) where {V}
        E = eltype(V)
        new{V, E}(a)
    end
end

```

? Or maybe something slightly different in terms of which constructors are defined, but you get the idea.

---

<div class="post-metadata">

**Author:** ![mhauru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhauru/32/208880_2.png) [@mhauru](https://discourse.julialang.org/u/mhauru)\
**Post date:** [August 14, 2020, 8:14am UTC](https://discourse.julialang.org/t/computed-parametric-field-types-using-assertions-in-getproperty/43865/13 "2020-08-14T08:14:51Z")

</div>

> [@mhauru](#):
>
> I just ran into some issues on GitHub that discuss problems that may arise from [defining `show` for types]

Should have looked into this a bit more: The docs for `show` explicitly tell us how to do this right:

> To customize human-readable text output for objects of type `T` , define `show(io::IO, ::MIME"text/plain", ::T)` instead.
