# Using containers with abstract type parameters

**URL:** <https://discourse.julialang.org/t/using-containers-with-abstract-type-parameters/60264>\
**Category:** General Usage\
**Created:** [April 29, 2021, 5:27pm UTC](https://discourse.julialang.org/t/using-containers-with-abstract-type-parameters/60264 "2021-04-29T17:27:46Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![anon60034542](https://avatars.discourse-cdn.com/v4/letter/a/90ced4/32.png) [@anon60034542](https://discourse.julialang.org/u/anon60034542)\
**Post date:** [April 29, 2021, 5:27pm UTC](https://discourse.julialang.org/t/using-containers-with-abstract-type-parameters/60264/1 "2021-04-29T17:27:46Z")

</div>

Consider the following code:

```julia
abstract type GameObject end
abstract type Weapon <: GameObject end

struct Sword <: Weapon
	name::String
    damage::Int32
end

struct Bow <:Weapon
    name::String
    damage::Int32
end

mutable struct Character
    name::String
    inventory :: Vector
	function Character(name::String)
		inventory = Vector{Weapon}()
		new(name,inventory)
	end
end

```

```julia
queen_svetlana = Character("Queen Svetlana")
push!(queen_svetlana.inventory,Sword("Sword",10), Bow("Bow",5))

```

As stated in [Performance Tips](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-abstract-container), it’s better to use a container with a concrete type, so instead of `Real`, you would use `Float64`.

The problem is in a situation where you want to keep an assortment of `Weapons` in one inventory, what is the efficient approach?

It seems kind of overkill to delcare a collection for each `Weapon` sub type:

```julia
swords = Vector{Sword}()
bow = Vector{Bow}()

```

In Performance Tips, it also states:

> If you cannot avoid containers with abstract value types, it is sometimes better to parametrize with `Any` to avoid runtime type checking.

The problem is, with `Any` you could also add `String` and `Int` for a container meant only for `Weapon`. You could work around the problem by writing your own `push!()` function that takes a character and weapon, but nothing stops you from accessing the container directly.

I guess some context might be useful here:

- Unlike a `Vector{Float64}`, we’re not doing any calculations with the `Vector{Weapons}`. We’re simply withdrawing a weapon to be used, and then putting it back afterwards.

- Using a `for` loop and `isa()` we might filter certain weapons, if you want a brief summary of specific weapon types, and `length(inventory)` to get the number of weapons.

Does the [Avoid containers with abstract type parameters](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-abstract-container) tip depend on how the container will be used? Given the above situation of adding, removing and simply looping, would using `Weapon` be okay?

---

<div class="post-metadata">

**Author:** ![Jordan\_Cluts](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jordan_cluts/32/13753_2.png) [@Jordan\_Cluts](https://discourse.julialang.org/u/Jordan_Cluts)\
**Post date:** [April 29, 2021, 6:07pm UTC](https://discourse.julialang.org/t/using-containers-with-abstract-type-parameters/60264/2 "2021-04-29T18:07:03Z")

</div>

Hopefully some other folks will respond with thoughts but here is my first take.

Do your different weapons need to be different types? I.E. are you dispatching on different weapons? If not you could instead make Weapon concrete and store the kind of weapon as a field

```julia
struct Weapon <: GameObject
  name::String
  damage::Int32
  kind
end

```

Then you could have

```julia
mutable struct Character
  name::String
  inventory::Vector{Weapon}
  function Character(name::String) = new(name,Vector{Weapon}())
end

queen_svetlana = Character("Queen Svetlana")
push!(queen_svetlana.inventory, Weapon("Sword",10, sword), Weapon("Bow",5,bow))

```

The `kind` field could be a string “sword”, “bow”, etc. or even better would be to use an enum  
`@enum WeaponType sword bow bigrock` the kind field could then be `kind::WeaponType`

On the other hand if you _are_ dispatching on weapon types than this wouldn’t work and hopefully some other kind folks will have some advice for you.

One other point: you have a “name” field in your weapons. Is this duplicating the type? as Sword(“Sword”) is a bit redundant if you are using them a bit like the `kind` field I described above.  
That is unless you literally mean to have named weapons like `Axe("Brian's Flaming Axe",8)` or `Sword("Sting",10)`.

---

<div class="post-metadata">

**Author:** ![Jordan\_Cluts](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jordan_cluts/32/13753_2.png) [@Jordan\_Cluts](https://discourse.julialang.org/u/Jordan_Cluts)\
**Post date:** [April 29, 2021, 6:27pm UTC](https://discourse.julialang.org/t/using-containers-with-abstract-type-parameters/60264/3 "2021-04-29T18:27:06Z")

</div>

Upon further reflection you can do one better and make Weapon a parametric type using the enum I described above.

```julia
struct Weapon{T} where {T<: WeaponType} <: GameObject
  name::String
  damage::Int32
end

mutable struct Character
  name::String
  inventory::Vector{Weapon{WeaponType}}
  function Character(name::String) = new(name,Vector{Weapon{WeaponType}}())
end

queen_svetlana = Character("Queen Svetlana")
push!(queen_svetlana.inventory, Weapon{Sword}("Fiery Sword",10), Weapon{Bow}("bow",5))

```

This lets you have a concrete array (I think) _and_ dispatch on weapon type if needed.

---

<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:** [April 29, 2021, 9:44pm UTC](https://discourse.julialang.org/t/using-containers-with-abstract-type-parameters/60264/4 "2021-04-29T21:44:00Z")

</div>

No this does not let you have a concrete array, it does not even parses. The syntax for the `struct` is:

```julia
julia> abstract type GameObject; end

julia> abstract type WeaponType <: GameObject; end

julia> struct Weapon{T <: WeaponType} <: GameObject
         name::String
         damage::Int32
       end

```

And if `WeaponType` is an abstract type or if it is an `Enum` (and you intend to do `Weapon{BOW}(...)` for example), both cases end up with `inventory::Vector{Weapon{WeaponType}}` being a container of an abstract type.

@anon60034542 If you need a container of objects of different types, just use a container of objects of different types. If you wanna them to be checked by type to be sure they are compatible when saved to the array, then pay the price for it. The only “solution” is what Jordan have pointed out before: transpose things from type-space to value-space. If you are not using the types for dispatch, and you do not need a way to extend the types of weapons externally (i.e., you have all the code in your hands, and you are not thinking about open-source modding), then you can create a concrete `Weapon` object with a field called `type` that has enum values inside it, and use this field in conditionals where it is needed.

---

<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:** [April 29, 2021, 9:55pm UTC](https://discourse.julialang.org/t/using-containers-with-abstract-type-parameters/60264/5 "2021-04-29T21:55:55Z")

</div>

> [@anon60034542](#):
>
> Unlike a `Vector{Float64}` , we’re not doing any calculations with the `Vector{Weapons}` . We’re simply withdrawing a weapon to be used, and then putting it back afterwards.

What makes using containers of abstract types slow is exactly the fact that “withdrawing a weapon to be used” (this is, getting an element from the container) is type-instable, i.e., the compiler cannot optimize any of the code using such weapon because it is not sure which weapon it is, the type of the weapon may vary in runtime so it always need to make slow dispatch look-ups when interacting in any way with the “withdrawn weapon”.

Maybe if you are just copying it to another point into the array or to another container then the loss is small but even so, the fact the container can contain multiple types will probably make it an array of references, instead of just having the objects contiguous in memory.

> [@anon60034542](#):
>
> Using a `for` loop and `isa()` we might filter certain weapons, if you want a brief summary of specific weapon types, and `length(inventory)` to get the number of weapons.

It would be good to benchmark this, using a solution with a container of abstract objects against a container of concrete objects that have `weapon_type` field. But I think that it should not matter for `length` (because this does not interact with the elements themselves) and the `for` with `isa` may be slower, but `isa` is a built-in function (so kinda magical) and maybe more optimized than most generic functions that need to pass by the dispatch system normally. I doubt `isa` can be faster than checking a `weapon_type` field in a concrete object, however, at most it will be equivalent.

---

<div class="post-metadata">

**Author:** ![Jordan\_Cluts](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jordan_cluts/32/13753_2.png) [@Jordan\_Cluts](https://discourse.julialang.org/u/Jordan_Cluts)\
**Post date:** [April 29, 2021, 10:29pm UTC](https://discourse.julialang.org/t/using-containers-with-abstract-type-parameters/60264/6 "2021-04-29T22:29:44Z")

</div>

> it does not even parse

Apologies, I was typing quickly to get the point across not to make perfectly syntax correct code. I should’ve made that clear so someone would know not to copy it exactly.

Thanks for letting her and I know it would still be an abstract type array. I apologize for my error.

---

<div class="post-metadata">

**Author:** ![anon60034542](https://avatars.discourse-cdn.com/v4/letter/a/90ced4/32.png) [@anon60034542](https://discourse.julialang.org/u/anon60034542)\
**Post date:** [April 30, 2021, 1:48pm UTC](https://discourse.julialang.org/t/using-containers-with-abstract-type-parameters/60264/7 "2021-04-30T13:48:36Z")

</div>

> Do your different weapons need to be different types? I.E. are you dispatching on different weapons? If not you could instead make Weapon concrete and store the kind of weapon as a field.

I chose `Sword` and `Bow`, because although both cause damage, both do so in different ways. I can swing my sword and hit an enemy or object (a shield, or another weapon), but with a bow, I can aim and then let my bow go from a distance, and use another bow (reloadable). You can look at it in many ways, for example, if I introduce `Battle-Axe`, that isn’t much different from a `Sword`, so instead of `Sword` I could say, `SwingableWeapons`.

> One other point: you have a “name” field in your weapons. Is this duplicating the type? as Sword(“Sword”) is a bit redundant if you are using them a bit like the `kind` field I described above.  
> That is unless you literally mean to have named weapons like `Axe("Brian's Flaming Axe",8)` or `Sword("Sting",10)` .

You’re correct! It’s meant for fantasy type naming, like `Ivan the Barbarian's Sword`.

---

<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:** [April 30, 2021, 1:52pm UTC](https://discourse.julialang.org/t/using-containers-with-abstract-type-parameters/60264/8 "2021-04-30T13:52:53Z")

</div>

Maybe I was too forcible, I apologize for that. I just did not want people with similar doubts ending up with the wrong impression after reading this thread.

> Thanks for letting her and I know it would still be an abstract type array. I apologize for my error.

Note the array itself is a concrete type, the problem is that the type of the elements it contains is not concrete, there is a distinction.

---

<div class="post-metadata">

**Author:** ![anon60034542](https://avatars.discourse-cdn.com/v4/letter/a/90ced4/32.png) [@anon60034542](https://discourse.julialang.org/u/anon60034542)\
**Post date:** [April 30, 2021, 2:40pm UTC](https://discourse.julialang.org/t/using-containers-with-abstract-type-parameters/60264/9 "2021-04-30T14:40:22Z")

</div>

Just to clarify, when you say:

> a container of objects of different types, just use a container of objects of different types. If you wanna them to be checked by type to be sure they are compatible when saved to the array, then pay the price for it

Do you mean like I’m doing it now with:

```julia
inventory = = Vector{Weapon}()
push!(queen_svetlana.inventory,Sword("Sword",10), Bow("Bow",5))

```

---

<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:** [April 30, 2021, 2:52pm UTC](https://discourse.julialang.org/t/using-containers-with-abstract-type-parameters/60264/10 "2021-04-30T14:52:22Z")

</div>

> [@anon60034542](#):
>
> Do you mean like I’m doing it now with:

Yes.

The thing is, if your case is really game development, then I really doubt the extra cost here is relevant (unless you are doing a simulation of huge armies swarming at each other). Your case seems to be “user is inspecting the weapons he have equipped in some character and maybe changing them” and in this case anything graphic (or even just printing the information in a terminal) will be much more costly than the cost of inspecting the list of weapons.

---

<div class="post-metadata">

**Author:** ![sylvaticus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sylvaticus/32/203883_2.png) [@sylvaticus](https://discourse.julialang.org/u/sylvaticus)\
**Post date:** [April 30, 2021, 8:51pm UTC](https://discourse.julialang.org/t/using-containers-with-abstract-type-parameters/60264/11 "2021-04-30T20:51:23Z")

</div>

And what about just using an Union ?

---

<div class="post-metadata">

**Author:** ![anon60034542](https://avatars.discourse-cdn.com/v4/letter/a/90ced4/32.png) [@anon60034542](https://discourse.julialang.org/u/anon60034542)\
**Post date:** [May 1, 2021, 4:28pm UTC](https://discourse.julialang.org/t/using-containers-with-abstract-type-parameters/60264/12 "2021-05-01T16:28:38Z")

</div>

So you would do this:

```julia
inventory = Vector{Weapon}()
SwordOrWeapon = Union{Sword,Weapon}
sword :: SwordOrWeapon = Sword("Normal Sword",10)
push!(inventory,sword)

```

Now, the real question is, when adding and removing from a `Vector{Weapon}`, does it suffer the same performance problems as indicated above?
