How do model a sub type that can fall under two different abstract types?

Suppose you’re building a medieval role playing game, you’re going to have weapons of different types, so you’ll create an abstract type called Weapon:

abstract type GameObject end
abstract type Weapon <: GameObject end

You decide that your charcters can cast spells, and spells also have different types, so you can do:

abstract type Spell <: Weapon end # Fireball, Freeze, etc...

The problem? Not every spell is considered a Weapon. Suppose you want a spell that manipulates nature, for example, a spell that makes a planet grow faster or a spell that makes spoiled food suddenly fresh again.

I could make Spell a direct sub type of GameObject, and then Nature and Defense

abstract type Spell <: GameObject end
abstract type Nature <: Spell # FreshFood, GrowPlant,etc...
abstract type Defense <: Spell # Fireball,Freeze, etc...

When you have a sub types that share attributes of different abstract types, how do you model it?

This brings to mind Eric Lippert’s Wizards and Warrriors series.

Just answered this in another thread: Why use subtypes instead of traits and duck typing? - #31 by Raf

You can do a kind of multiple inheritance with subtyped traits. Edit: But this is probably overcomplicating things normal traits are all you need here.

You could also use regular object composition and multiple dispatch.


My first response is to generally caution you about overusing complex type hierarchies. In
general, types should be used strictly for dispatch; until you have a function that you need
to dispatch differently for one set of types versus another, abstract parent types shouldn’t
be introduced.

But if you do need to control dispatch in a way where simple single-inheritance won’t work,
you can use a “Holy trait”:

struct HasMyTrait end
struct HasntMyTrait end

mytrait(_) = HasntMyTrait()

struct MyType end
mytrait(::MyType) = HasMyTrait()

myfunc(x) = _myfunc(x, mytrait(x))

_myfunc(x, ::HasntMyTrait) = ... # Behavior for types without trait
_myfunc(x, ::HasMyTrait) = ... # Behavior for types with trait

There are several packages that try to wrap this pattern into a macro to make it more
pleasant to use, but I’m not personally familiar enough with any of them to make a