Defining composite Blox with @composite
A composite blox is a blox that is really a graph of sub-blox. @composite combines the two macros you already know:
- the header is a constructor signature, exactly like
@blox; - the body is an
@nodes/@connectionsgraph, exactly like@graph.
@composite struct StructName(; name, namespace=nothing, other_kwargs...)
# optional setup code (runs in the constructor, before the graph is built)
@nodes begin
(...)
end
@connections begin
(...)
end
[@extra_fields begin (...) end]
endIf you've read the @graph docs, the @nodes and @connections blocks behave identically here — same node forms (single call, comprehension, for-block), same connection syntax. Namespacing is handled for you: nodes inherit the composite's namespace automatically, so you don't write namespace = ... on them.
What's different from @graph
Supertype defaults to
AbstractComposite. As with@blox, you may declare a supertype after the header (StructName(; ...) <: SuperType) to override this; omit it and the struct subtypesAbstractCompositedirectly.Nodes become fields. Every top-level assignment in
@nodesbecomes a field of the struct, so sub-components are reachable by name:str = Striatum(; name=:str, N_inhi=25) str.matrisome # the sub-blox named `matrisome` str.inhibs[1] # comprehension nodes are stored as-is (here, a vector)Four fields are always present:
name::Symbol,namespace::Union{Symbol,Nothing},graph::GraphSystem,metadata::Dict{Symbol,Any}— then the node fields, in declaration order.metadatais automatic, just like@blox: ametadata=Dict{Symbol,Any}()keyword argument is added to the constructor for you.
Extra fields with @extra_fields
Use an optional @extra_fields block to add struct fields beyond the node fields. Each entry is an assignment field = value, optionally typed as field::T = value. These fields are appended after the node fields, and their right-hand sides are evaluated in the constructor after the graph is built — so they may reference the node variables bound by @nodes.
@extra_fields begin
pv_cells = [wta.pv for wta ∈ wta_circuits]
cluster_label::Symbol = cluster_label
endThis is the composite counterpart of @blox's @extra_fields: a place to cache derived handles (e.g. a flattened list of specific sub-neurons) or to stash a constructor argument on the struct for later use.
Example
@composite struct Striatum(; name, namespace=nothing, N_inhi=25,
E_syn_inhi=-70, G_syn_inhi=1.2, I_bg=zeros(N_inhi), τ_inhi=70.0)
I_bg = I_bg isa AbstractArray ? I_bg : fill(I_bg, N_inhi)
@nodes begin
inhibs = [HHInhiNeuron(name=Symbol(:inhi, i), I_bg=I_bg[i]) for i ∈ 1:N_inhi]
matrisome = Matrisome(; name=:matrisome)
striosome = Striosome(; name=:striosome)
end
@connections begin
# ... wiring between inhibs / matrisome / striosome ...
end
endinhibs, matrisome, and striosome are fields of Striatum; every neuron lands in the composite's namespace with no namespace plumbing written by hand.