# Correct usage of type-dependent constructors

**URL:** <https://discourse.julialang.org/t/correct-usage-of-type-dependent-constructors/128605>\
**Category:** General Usage\
**Tags:** struct\
**Created:** [May 1, 2025, 10:53pm UTC](https://discourse.julialang.org/t/correct-usage-of-type-dependent-constructors/128605 "2025-05-01T22:53:16Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![albertomercurio](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/albertomercurio/32/27051_2.png) [@albertomercurio](https://discourse.julialang.org/u/albertomercurio)\
**Post date:** [May 1, 2025, 10:53pm UTC](https://discourse.julialang.org/t/correct-usage-of-type-dependent-constructors/128605/1 "2025-05-01T22:53:16Z")

</div>

Hello,

I need to create a constructor with a parametric type to be able to dispatch. One possible way is the following

```julia-auto
abstract type MyType end

# Singletons occupying 0 bytes
struct Type1 <: MyType end
struct Type2 <: MyType end

struct MyStruct{T<:MyType,MT<:AbstractArray}
    data::MT
    type::T
end

```

and I can call it with `x = MyStruct(rand(10), Type1())` for example.

I could even remove the `type` field from the struct, and just leave the parametric type, but I prefer to be able to call `x.type`.

Now, do I really need to define the constructors to just have some types to use for multiple dispatch? Is the use of “standard” types better here? I mean something like

```julia-auto
abstract type MyType end

# or primitive types?
abstract type Type1 <: MyType end
abstract type Type2 <: MyType end

struct MyStruct{T<:MyType,MT<:AbstractArray}
    data::MT
    type::Type{T} # Note the Type here
end

```

This would allow to define the object without parentheses, with something like `x = MyStruct(rand(10), Type1)`, which I prefer.

Do you recommend this second method?

## Alternative method

Or can I just do a mix of the two?

```julia-auto
abstract type MyType end

# Singletons occupying 0 bytes
struct Type1 <: MyType end
struct Type2 <: MyType end

struct MyStruct{T<:MyType,MT<:AbstractArray}
    data::MT
    type::Type{T} # Note the Type here
end

```

and then calling `MyStruct(rand(10), Type1)` so that I don’t have to put the parentheses.

Which method do you recommend? Is it a good way to work with types rather than `struct`s?

If this method works, I was trying to implement the following function

```julia-auto
function MyStruct(data; type::Union{Nothing, Type{T}} = nothing) where T
    if isnothing(type)
        _type = Type1
    else
        _type = T
    end
           
    return MyStruct(data, _type)
end

```

But I get type instabilities when doing `MyStruct(rand(3); type=Type2)` for example.

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [May 1, 2025, 11:52pm UTC](https://discourse.julialang.org/t/correct-usage-of-type-dependent-constructors/128605/3 "2025-05-01T23:52:57Z")

</div>

You could do

```julia
abstract type MyType end

struct Type1 <: MyType end
struct Type2 <: MyType end

struct MyStruct{T <: MyType, MT <: AbstractArray}
    data::MT
end
    
MyStruct{T}(data::MT) where {T <: MyType, MT <: AbstractArray} = MyStruct{T,MT}(data)

```

This way you don’t have type instabilities. Moreover, you don’t have a `type::Type{T}` field that occupies memory (8 bytes). Whether `Type1` and `Type2` themselves consume memory doesn’t matter.

You’ve said that you want to say `x.type`. What’s wrong with

```julia
mytype(x::MyStruct{T}) where T = T

```

If you insist on the `x.type` notation, you could say

```julia
function Base.getproperty(x::MyStruct{T}, name::Symbol) where T <: MyType
    name == :type ? T : getfield(x, name)
end

```

but I wouldn’t do it.

ADDED: Instead of defining new types like `Type1` or `Type2`, you can also use integers or symbols as type parameters. That often suffices in simple cases.

---

<div class="post-metadata">

**Author:** ![albertomercurio](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/albertomercurio/32/27051_2.png) [@albertomercurio](https://discourse.julialang.org/u/albertomercurio)\
**Post date:** [May 2, 2025, 12:03am UTC](https://discourse.julialang.org/t/correct-usage-of-type-dependent-constructors/128605/4 "2025-05-02T00:03:26Z")

</div>

Hi @matthias314,

Thank you for your reply.

I actually still see some type instabilities when using a function with keyword arguments like

```julia
function MyStruct(data; type::Type{T}=Type1) where T<:MyType
    return MyStruct{T}(data)
end

```

In general it will be more complicated, doing different things depending on the `type` argument.

I don’t have any type instabilities when using the `struct` as a variable, like

```julia
function MyStruct(data; type::T=Type1()) where T<:MyType
    return MyStruct{T}(data)
end

```

which is ok, but I would try keep using only types (if possible).

Moreover, I really need to have defined `x.type`, as I have to align with another package in Python that has this. Do you recommend to put it as a field, or defining a custom `Base.getproperty`?

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [May 2, 2025, 12:15am UTC](https://discourse.julialang.org/t/correct-usage-of-type-dependent-constructors/128605/5 "2025-05-02T00:15:56Z")

</div>

> [@albertomercurio](#):
>
> I actually still see some type instabilities when using a function with keyword arguments

As far as I know, there is no clean way around this. It’s a deficiency of keyword arguments. However, if you parametrize your type by, say, symbols, then you can say something like

```julia
function MyStruct(data; type::Val{S} = Val(:default)) where S
    return MyStruct{S}(data)
end

```

EDIT: This also works with types instead of symbols.

Is the keyword argument important? Otherwise you could just define

```julia
MyStruct(data) = MyStruct{Type1}(data)

```

> Do you recommend to put it as a field

I don’t know. If your parameter types don’t need memory, then a `type` field would be OK, I guess. Your goal seems to be to imitate some Python code, so it’s hard to tell from the outside. In any case, changing from one approach to the other should only require changing a few lines of code, so you try out both.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [May 2, 2025, 7:02am UTC](https://discourse.julialang.org/t/correct-usage-of-type-dependent-constructors/128605/6 "2025-05-02T07:02:32Z")

</div>

> [@albertomercurio](#):
>
> ```julia
> # or primitive types?
> abstract type Type1 <: MyType end
> abstract type Type2 <: MyType end
> 
> ```

`abstract` or `primitive` types don’t have any benefit over normal concrete `struct`s here, they all subtype `DataType`.

> [@matthias314](#):
>
> It’s a deficiency of keyword arguments.

The compiler tries its best to maintain `Type{T}` information, but because `typeof(T)` is `DataType` (or maybe `Union` or `UnionAll`), it gets lost when it’s used to construct a container like an `Array`. In keyword arguments’ case, it goes through a `NamedTuple`. The instance is usually stored for this reason.

> [@albertomercurio](#):
>
> I could even remove the `type` field from the struct, and just leave the parametric type, but I prefer to be able to call `x.type`.

It seems like you’re effectively embedding a Holy trait-like instance or type into `MyStruct` for the purpose of dispatch. I know that this is probably a reduced example, so the following opinions may be unsuitable. Nevertheless, here are some alternatives I find more suitable:

1. Since the trait isn’t necessary for the data of the structure (0 bytes by intention), it may not even need to be part of the type. Multiple dispatch after all lets you dispatch on `MyStruct` and the trait as separate arguments or even a 2-tuple. The benefit of not having to dispatch on the trait is type stability when the trait isn’t needed and varies at runtime.
2. Currently you have concrete `MyStruct{T, MT}` subtyping `MyStruct{<:Any, MT}` and concrete `T` subtyping `MyType`. You could simplify that into `MyType` subtypes containing the `data` directly, combining the trait and the data permanently. If you had a fixed set of `MyType` subtypes, this would probably be the expected move. If you want to vary the exact `data` structure, even across `MyType`s, you can abstract that away with interface methods.

---

<div class="post-metadata">

**Author:** ![albertomercurio](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/albertomercurio/32/27051_2.png) [@albertomercurio](https://discourse.julialang.org/u/albertomercurio)\
**Post date:** [May 2, 2025, 8:13am UTC](https://discourse.julialang.org/t/correct-usage-of-type-dependent-constructors/128605/7 "2025-05-02T08:13:49Z")

</div>

> [@matthias314](#):
>
> However, if you parametrize your type by, say, symbols

That was a good idea, but I would avoid defining everything with `Val(Symbol)`. So I think that directly defining the `struct` as an argument is the safest option.

> [@Benny](#):
>
> `abstract` or `primitive` types don’t have any benefit over normal concrete `struct`s

Good to know, thanks.

> [@Benny](#):
>
> Since the trait isn’t necessary for the data of the structure (0 bytes by intention), it may not even need to be part of the type. Multiple dispatch after all lets you dispatch on `MyStruct` and the trait as separate arguments or even a 2-tuple. The benefit of not having to dispatch on the trait is type stability when the trait isn’t needed and varies at runtime.

Could you be more explicit here?

> [@Benny](#):
>
> Currently you have concrete `MyStruct{T, MT}` subtyping `MyStruct{<:Any, MT}` and concrete `T` subtyping `MyType`. You could simplify that into `MyType` subtypes containing the `data` directly, combining the trait and the data permanently. If you had a fixed set of `MyType` subtypes, this would probably be the expected move. If you want to vary the exact `data` structure, even across `MyType`s, you can abstract that away with interface methods.

I still prefer to keep them separate, such that I can do `x.data` and `x.type` separately.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [May 2, 2025, 8:22am UTC](https://discourse.julialang.org/t/correct-usage-of-type-dependent-constructors/128605/8 "2025-05-02T08:22:55Z")

</div>

> [@albertomercurio](#):
>
> Could you be more explicit here?

Calls would look like `foo(mystruct, mytype)` or `foo( (mytype, mystruct) )`, the data in `MyStruct` is entirely independent of the trait `MyType`.

---

<div class="post-metadata">

**Author:** ![albertomercurio](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/albertomercurio/32/27051_2.png) [@albertomercurio](https://discourse.julialang.org/u/albertomercurio)\
**Post date:** [May 2, 2025, 11:33am UTC](https://discourse.julialang.org/t/correct-usage-of-type-dependent-constructors/128605/9 "2025-05-02T11:33:29Z")

</div>

Ok, thanks.

I think I will continue using the `struct` inside as before.

Thanks anyway to both of you. Everything is more clear now.
