How to do arithmetic in the type system?

I want to declare a struct with:

struct MyStruct{L,P}
    a::SVector{L,Other1}
    b::SVector{P,Other2}
    c::SVector{L+P,Float64}
end

The third field gives error: MethodError: no method matching +(::TypeVar, ::TypeVar)

How do I do arithmetic in the type system?

you can’t, Julia does not have dependent type.

1 Like

https://github.com/vtjnash/ComputedFieldTypes.jl

3 Likes

Instead of copying the answers from the previous discussion, I’ll just link it:

1 Like

In addition to what others have said, you can mostly achieve what you’re looking for with an extra type parameter, but it is inconvenient:

struct MyStruct{L,P, N}
    a::SVector{L,Other1}
    b::SVector{P,Other2}
    c::SVector{N,Float64}

    function MyStruct{L, P, N}(...)
        # Check that N == L + P here
        ....
    end
end


# Convenient outer constructor that computes N for you
function MyStruct{L, P}(...) where {L, P}
    MyStruct{L, P, L + P}(...)
end
5 Likes

Can you define an outer constructor with partial parametric types? Does this have well-defined semantics/support?

I hope it is, I have been just implementing a bunch of stuff using that, without even thinking about it :grimacing:

But what I wanted to add is that the additional parameter is sort of ugly, but you can safely ignore it for dispatch, i. e., you can use something like

f(x::MyStruct{L,P}) where {L,P} = ... 

ignoring the fact that there is a third parameter L+P, and that is supported. So, the combination of a convenient constructor with this pattern makes carrying the additional parameter not as cumbersome as it might seem.

edit: @Henrique_Becker, isn’t that what the StaticMatrix constructors are? https://github.com/JuliaArrays/StaticArrays.jl/blob/1858207b405ea168ecd6810ffa7bbb07e984a2b7/src/SMatrix.jl#L17

(the SMatrix type has 4 parameters, the last one being sort of redundant, and there are several constructors that use that pattern to skip it in the API).

I am not entirely sure because of the extensive use of generators in that piece of code.

But these partial constructors are known to have some bugs, the two of us already found some strange behavior on then in the past, see Using parametric type inside outer parametric constructor - #28 by Henrique_Becker and https://github.com/JuliaLang/julia/issues/39280

1 Like

Yeah, absolutely. The parameters in the outer constructor just have to be explicitly supplied by the user at the call site:

julia> struct Foo{X, Y} end

julia> function Foo{X}() where {X}
         Foo{X, Int}()
       end

julia> Foo{Float64}()
Foo{Float64, Int64}()

Note that I was missing the where clause in my outer constructor originally. I’ll edit the post to fix that.

I hope so, but I couldn’t find anything in the manual about that. This is probably very common (and very useful), maybe it should be better documented?

1 Like

I agree with Leandro, the implementation has some subtle bugs, there is no mention in the documentation, and very simple code that I would guess on working simply does not work.

EDIT: forgot where in the code.

1 Like

You need the where A there:

julia> struct S{A, B}; a :: A; b :: B; end

julia> S{A}(a) where A = S{A,Int}(a,0)

julia> S{Int}(1)
S{Int64, Int64}(1, 0)