This note is about the use of a Type as a supertype
when defining a struct
.
Let’s select Integer
to be our exemplar supertype
.
Here is very simple struct
that is using Integer
as its supertype
.
struct BehavesLikeAnInteger <: Integer
value::Int
end
intlike = BehavesLikeAnInteger(5)
# BehavesLikeAnInteger(5)
Here is almost the same thing – now gone horribly wrong.
struct BehavesLikeAnInteger <: Integer
value::String
end
notatall_intlike = BehavesLikeAnInteger("The marmalade is talking.")
# BehavesLikeAnInteger("The marmalade is talking.")
The Type that is used as the supertype
for a struct
is how we say:
“This struct
of mine embodies the intent that Type evinces/evokes.
Moreover, the information that I keep each time that this struct
is constructed, provides the wherewithall to participate in operations
that expect some kind of [are designed to accept an] Integer
.”
So the choice of a supertype
is not bringing to bear the operational
functionality of the type, rather, it is bringing your struct
into
the operational fold of that supertype
. It is incumbent upon the
designer of a struct
with supertype
to provide the functionality
necessary for the struct
“to play well with” extant methods.
Often, this is accomplished by forwarding the method through e.g.
the value
field of a struct
. Here is an example.
struct BehavesLikeAnInteger <: Integer
value::Int
end
# iszero(x) returns a Bool, no re-construction is needed
Base.iszero(x::BehavesLikeAnInteger) =
iszero(x.value)
# abs(x) returns a typeof(x), re-construction is needed
Base.abs(x::BehavesLikeAnInteger) =
BehavesLikeAnInteger(abs(x.value))
This gets tedious very quickly. I wrote TypedDelegation.jl
for just this reason. That package makes type respectful
delegation through the field[s] of a struct easy to express.
There are examples of how it is used in the README.md file.
Another reason to choose a supertype for your struct is that
the supertype is available for use in guiding multidispatch.
abstract type VideoGame end
abstract type SinglePlayerGame <: VideoGame end
abstract type MultiPlayerGame <: VideoGame end
struct Pacman <: SinglePlayerGame
<...>
player::GamePlayer
end
struct RedDeadRedemption2 <: MultiplayerGame
<...>
players::Vector{GamePlayer}
end
function gameplayer(game::SinglePlayerGame, player::GamePlayer) ... end
function gameplayers(game::MultiPlayerGame, players::Vector{GamePlayer}) ... end
Register all your players, in one [actually two] fell swoop,
independent of the specific game being played, dispatched by
the virtual trait isita_singleplayergame
implemented through
inheritance and dispatch.