I am new to Julia and therefore my curiosity in some implementation details.
In the package Agents.jl, the type constructor of Agents.AgentBasedModel is expecting a type as its first argument (specifically an AgentType <: AbstractAgent
).
I am wondering what are the advantages of or motivation behind following this way rather than qualifying the agent type as a type parameter for the type AgentBasedModel, i.e.
AgentBasedModel{AgentType}(...) where AgentType <: AbstractAgent
?
I guess the same question applies to the second argument of the constructor, i.e. space type could have been also qualified as a parameter?
Hi, its great that you are curious! I hope the answer “its a matter of style” won’t dissapoint you though
-
AgentBasedModel
has several other type parameters besides the agent type, and therefore specifiying only one of the first in {}
does not appear very intuitive to me.
- It makes for a simpler API if everything is an argument to a function, rather than half the things being arguments to functions and the other half being type parameters. Said differently, I believe that
ABM{x1}(x2)
is fundamentally more complex expression than ABM(x1, x2)
. Internally we can transfer x1
to be type parameter if necessary.
- Even if you did specified the type of the other arguments, like the space type, you would still need to give the actual space instance to the function as its actual values, besides its type, are needed. So you would need to write
ABM{A, S}(s::S)
, i.e., repeat the information S
twice, which is a bit pointless if you think about it.
4 Likes
Hi,
Thanks for the illustrative reply!
No it does not disappoint me at all.
With respect to the space type, the first time I gone over the concept, I was also thinking that some information is kind of redundant till I realised that there are situations where space type at agent level can be different than the space type at the model level. So this is completely fine.
As a Julia newbie, I kind of find it easier to code to have a parameterised type:
mutable struct AgentBasedModel{AgentType <: AbstractAgent}
agentList::Array{AgentType,1}
...
end # ABM
if the AgentType is a field of the struct, i.e.
mutable struct AgentBasedModel
AgentType
# Here some fancy advanced code probably meta-programming
# to guarantee that the client provides a substype of AbstractAgent
agentsList
# Again some advanced code to guarantee that we have
# agents of AgentType in the list ..
# and probably Julia is clever enough the performance is not negatively
# influenced following this approach (and may be it is better as you hinted
# as ABM will not be dispatched to a combinatoric number of types?)
...
end # ABM
I will hopefully look-up the implementation so that I get to know how this is done. But basically with this question, I am trying as a newbie to eliminate years of experiences I need so that I decide a similar approach to similar situations rather than deciding that in the next decade
you are confusing the public api with the internal implementation. Of course ABM is implemented as
mutable struct AgentBasedModel{AgentType <: AbstractAgent}
agentList::Array{AgentType,1}
...
end # ABM
but this doesn’t mean that the front end user should go through the same complexity to initialize an ABM.
1 Like
Ok! I will need to have a look over the implementation.