This enhancement replaces the RelationshipTarget struct with a (new) HolonCollection enum. A HolonCollection is either editable (i.e., being staged) or existing (i.e., read-only).
Current State
SmartCollections:
represent the target of an existing relationship (persisted as SmartLinks) for a source_holon: HolonReference
are intended to be read-only -- i.e., holons may not be added or removed from the collection
contain a vector of SmartReferences that may only be loaded from SmartLinks retrieved from the persistence layer.
StagedCollections:
represent the target of relationships that have NOT yet been persisted as SmartLinks
can ONLY be the targets for a Staged source_holon: StagedReference
contain a vector of HolonReferences that refer to new OR existing holons
holon references may be added or removed from the collection
In effect, SmartCollections are loadable and previously persisted , whereas StagedCollections are editable and not previously persisted.
Beyond these differences, they are basically identical:
both support get_by_key
both should support query operations whose results may be filtered, sorted and limited.
both are targets of a relationship for a source_holon
neither can have an independent existence apart from a source holon's RelationshipMap. This suggests it may not be necessary to store the _sourceholon or the relationship reference in the Collection struct. In other words, it's not clear we have any use case that requires navigation from a collection BACK to its source holon.
Analysis of Design Options
Because this enhancement is complex enough to warrant consideration of different design options, they are briefly discussed here:
Whether to follow the State Design Pattern
Whether to rename RelationshipTarget to HolonCollection
Use State Pattern?
Should we:
follow the State design pattern and retain separate struct definitions for StagedCollection and SmartCollection (wrapped in HolonCollection enum) and with shared behaviors defined in a CollectionGettable trait?
Or should we unify both into a single HolonCollection struct with a state variable and is_accessible method that guards access to functions.
Given the high degree of shared functionality and the likely need to have a state variable to handle commit processing states, I'm leaning towards Option 2.
Rename RelationshipTarget to HolonCollection?
This simplifies things quite a bit, but may pose an evolvability barrier if we decide to return to supporting different cardinalities (Optional, One, Many) for relationship targets. HolonCollection really only applies to the Many variant.
There are no current plans to return to this concept and for now it seems worth the simplification.
Proposal
Unify SmartCollection and StagedCollection into HolonCollection
In a new _holoncollection.rs file in the holons zome
[x] Introduce a new CollectionState enum
pub enum CollectionState {
Fetched, // links have been fetched from the persistent store for this collection
Staged, // the links for this collection have not been persisted
Saved, // a staged collection for which SmartLinks have been successfully committed
Abandoned, // a previously staged collection that was abandoned prior to being committed
}
[x] Introduce a new HolonCollection struct
pub struct HolonCollection {
state: CollectionState,
members: Vec<HolonReference>,
keyed_index: BTreeMap<MapString, usize>, // usize is an index into the members vector
}
Notice that all fields are private and that _sourceholon, _relationshipdescriptor and _accesspath have been dropped from this definition.
HolonCollection should support the following functions:
[x] is_accessible(&self, access_type: AccessType)
uses the collection's state to determine whether the requested operation is allowed. Its logic can follow that of Holon's is_accessible method and use the same HolonError code. Here are the rules:
Read access is allowed for Fetched, Staged, and Saved
this function requires that self is accessible for Read. It creates a new Staged HolonCollection from an existing Fetched collection by cloning the Fetched collection and then changing its state to Staged.
this function requires that self is accessible for Read. It returns a HolonReference to the holon whose key matches the supplied key. If no such holon exists, return None.
this function requires that self is accessible for Write. It iterates through the supplied holon references and, for each reference:
adds the holon reference to the collection
calls get_key() on the holon_reference and, if it has one, updates the collection's keyed_index
Address Implementation Impacts
Neither SmartCollection nor StagedCollection are broadly used, so the effects of this change are somewhat limited. However, to realize the benefits of simplification, we need to leverage the new capabilities to drive simplification in several files.
_In stagedreference.rs:
[x] Drop the ensure_editable_collection method
[x] Simplify add_related_holons method:
If the relationship_map has an entry for the specified relationship, get a mut reference to its HolonCollection and invoke the collection's add_references method.
Otherwise, create an empty HolonCollection, invoke add_references on it, then add an entry for the specified relationship to the StagedReference's relationship_map.
Refactor RelationshipTarget to HolonCollection (move to separate issue?)
[x] Refactor RelationshipTarget to HolonCollection
replace all usages of RelationshipTarget to use HolonCollection instead
This enhancement replaces the RelationshipTarget struct with a (new) HolonCollection enum. A HolonCollection is either editable (i.e., being staged) or existing (i.e., read-only).
Current State
In effect, SmartCollections are loadable and previously persisted , whereas StagedCollections are editable and not previously persisted.
Beyond these differences, they are basically identical:
get_by_key
Analysis of Design Options
Because this enhancement is complex enough to warrant consideration of different design options, they are briefly discussed here:
State
Design PatternUse State Pattern?
Should we:
is_accessible
method that guards access to functions.Given the high degree of shared functionality and the likely need to have a state variable to handle commit processing states, I'm leaning towards Option 2.
Rename RelationshipTarget to HolonCollection?
This simplifies things quite a bit, but may pose an evolvability barrier if we decide to return to supporting different cardinalities (
Optional
,One
,Many
) for relationship targets. HolonCollection really only applies to theMany
variant.There are no current plans to return to this concept and for now it seems worth the simplification.
Proposal
Unify SmartCollection and StagedCollection into HolonCollection
In a new _holoncollection.rs file in the holons zome
Notice that all fields are private and that _sourceholon, _relationshipdescriptor and _accesspath have been dropped from this definition.
HolonCollection should support the following functions:
[x]
is_accessible(&self, access_type: AccessType)
uses the collection's
state
to determine whether the requested operation is allowed. Its logic can follow that of Holon'sis_accessible
method and use the same HolonError code. Here are the rules:Read access is allowed for
Fetched
,Staged
, andSaved
Write access is allowed for
Staged
[x]
into_staged(&self)->Result<HolonCollection, HolonError>
this function requires that self is accessible for Read. It creates a new
Staged
HolonCollection from an existingFetched
collection by cloning the Fetched collection and then changing its state toStaged
.[x]
get_by_key(&self, key: &MapString)->Result<Option<HolonReference>, HolonError>
this function requires that self is accessible for Read. It returns a HolonReference to the holon whose key matches the supplied key. If no such holon exists, return None.
[x]
add_references(&self, holons: Vec<HolonReference>)->Result<(), HolonError>
this function requires that self is accessible for
Write
. It iterates through the supplied holon references and, for each reference:get_key()
on the holon_reference and, if it has one, updates the collection's keyed_indexAddress Implementation Impacts
Neither SmartCollection nor StagedCollection are broadly used, so the effects of this change are somewhat limited. However, to realize the benefits of simplification, we need to leverage the new capabilities to drive simplification in several files.
_In stagedreference.rs:
ensure_editable_collection
method[x] Simplify
add_related_holons
method:add_references
method.add_references
on it, then add an entry for the specified relationship to the StagedReference's relationship_map.Refactor RelationshipTarget to HolonCollection (move to separate issue?)
[x] Refactor RelationshipTarget to HolonCollection
replace all usages of RelationshipTarget to use HolonCollection instead