@kristoffer.carlsson well, that’s what I thought too. Here’s the source code:
abstract type Blueprint end
struct ConsumableBlueprint <: Blueprint
type_id::UUID
name::String
ConsumableBlueprint(name::String) = new(uuid4(), name)
end
struct ProducerBlueprint <: Blueprint
type_id::UUID
name::String
lifecycle::Restorable
restore_res::Dict{Blueprint,Int64}
restore::Float64
batch_req::Dict{Blueprint,Int64} # Required input per batch
batch::Dict{Blueprint,Int64} # batch per batch. The Blueprint and the number of items per blueprint.
max_production::Int64 # Max number of batches per production cycle
ProducerBlueprint(
name::String,
lifecycle::Restorable = Restorable();
restore_res::Dict{Blueprint,Int64} = Dict{Blueprint,Int64}(),
restore::Real = 0,
batch_req::Dict{Blueprint,Int64} = Dict{Blueprint,Int64}(),
batch::Dict{Blueprint,Int64} = Dict{Blueprint,Int64}(),
max_production::Int64 = INF
) = new(uuid4(), name, lifecycle, restore_res, restore, batch_req, batch, max_production)
end
ERROR: TypeError: in keyword argument batch_req, expected Dict{Blueprint,Int64}, got a value of type Dict{ConsumableBlueprint,Int64}
Stacktrace:
[1] top-level scope at none:1
In your example, when you try to build factory_bp you set a field with batch_req = Dict(labour_bp => 2), but labour_bp at that time is concretely typed as ConsumableBlueprint, so the resulting Dict is not a Dict{Blueprint,Int64} it’s a Dict{ConsumableBlueprint,Int64}. You have at least a few choices depending on how it’s going to be used.
If ProduceBlueprint is only going to read from the Dict, then its fields could be changed to Dict{<:Blueprint,Int64} so that this kind of input is allowed. This would require changing the fields of the struct and of its constructor.
If ProduceBlueprint needs to be able to write arbitrary blueprints to the Dict objects it receives, then you need batch_req = Dict{Blueprint,Int64}(labour_bp => 2)
Add an outer constructor for which restore_res, restore, and batch are untyped and convert(Dict{Blueprint, Int64}, batch) them before passing to the inner constructor
@malacroi, I went for the first solution since ProducerBlueprint doesn’t need to write anything to the Dict.
So I thought I could do something similar for Entities:
struct Entities
entities::Dict{Blueprint,Vector{Entity}}
Entities() = new(Dict{Blueprint,Vector{Entity}}())
end
function Entities(resources::Dict{<:Blueprint,Vector{<:Entity}})
entities = Entities()
for blueprint in keys(resources)
for entity in resources[blueprint]
push!(entities, entity)
end
end
return entities
end
Base.keys(entities::Entities) = keys(entities.entities)
Base.values(entities::Entities) = values(entities.entities)
Base.getindex(entities::Entities, index::Blueprint) = entities.entities[index]
Base.setindex!(entities::Entities, e::Array{Entity,1}, index::Blueprint) = (entities.entities[index] = e)
function Base.push!(entities::Entities, entity::Entity)
if entity.blueprint in keys(entities)
push!(entities[entity.blueprint], entity)
else
entities[entity.blueprint] = Vector{Entity}([entity])
end
return entities
end
function Base.pop!(entities::Entities, blueprint::Blueprint)
if blueprint in keys(entities)
e = pop!(entities[blueprint])
if length(entities[blueprint]) == 0
pop!(entities.entities, blueprint)
end
return e
else
return nothing
end
end
where Consumable <: Entity, I get the following error:
ERROR: MethodError: no method matching Entities(::Dict{ConsumableBlueprint,Array{Consumable,1}})
Closest candidates are:
Entities() at /Users/stef/Programming/Julia Projects/loreco-abm/cockpit/production/entities.jl:76
Entities(::Dict{var"#s50",Array{var"#s49",1} where var"#s49"<:Entity} where var"#s50"<:Blueprint) at /Users/stef/Programming/Julia Projects/loreco-abm/cockpit/production/entities.jl:79
Stacktrace:
[1] top-level scope at none:1
var"#s50" is labour_bp which is <: Blueprint and var"#s49" are the 2 Consumables which are both <: Entity
@Tamas_Papp That’s the thing, I did define an outer constructor which takes a Dict:
function Entities(resources::Dict{<:Blueprint,Vector{<:Entity}})
entities = Entities()
for blueprint in keys(resources)
for entity in resources[blueprint]
push!(entities, entity)
end
end
return entities
end
abstract type Blueprint end
struct ABlueprint <: Blueprint
val::Int64
ABlueprint(val::Int64=0) = new(val)
end
abstract type Entity end
struct AnEntity <: Entity
val::Int64
AnEntity(val::Int64=0) = new(val)
end
struct Entities
entities::Dict{Blueprint,Vector{Entity}}
Entities() = new(Dict{Blueprint,Vector{Entity}}())
end
function Entities(resources::Dict{<:Blueprint,Vector{<:Entity}})
entities = Entities()
for blueprint in keys(resources)
for entity in resources[blueprint]
push!(entities, entity)
end
end
return entities
end
Entities(Dict(ABlueprint() => [AnEntity()]))
The last line results in the following error:
ERROR: MethodError: no method matching Entities(::Dict{ABlueprint,Array{AnEntity,1}})
Closest candidates are:
Entities() at none:3
Entities(::Dict{var"#s10",Array{var"#s9",1} where var"#s9"<:Entity} where var"#s10"<:Blueprint) at none:1
Stacktrace:
[1] top-level scope at none:1
When calling the methods() function I get the following output:
methods(Entities)
# 2 methods for type constructor:
[1] Entities() in Main at none:3
[2] Entities(resources::Dict{var"#s10",Array{var"#s9",1} where var"#s9"<:Entity} where var"#s10"<:Blueprint) in Main at none:1
My assumption was that I could therefore call the outer constructor of Entities() with a Dict that contains implementations of Blueprint and Entity.
That said, since you are traversing the input anyway, I think it is better to just allow all kinds of arguments that support pairs and use that interface.