Hi everyone,
I am currently working on the implementation of a BMM-based runtime. As I map out the memory model and semantic analysis logic, I have identified a few patterns in the BMM specification that I’d love to get your thoughts on. I want to ensure my implementation aligns with the intended architectural vision of openEHR.
1. Representation of Generic Containers
in BMM spec it states that inheritance is from class to types, and it is very logical. The types of container classes are represented using BMM_CONTAINER_TYPE: BMM_TYPE, on the other hand BMM_CLASS::get_ancestors(): Hash<string, BmmModelType>, so ancestors are represented using BMM_MODEL_TYPE, which inherits BMM_UNITARY_TYPE rather that BMM_CONTAINER_TYPE which inherit BMM_TYPE directly.
based on that what do you think is the best way to represent for example class List<T>: Container<T>, one way in my mind is to make an exception for declartion cases like this one and represent the ancestor Container<T> as below do think this is an acceptable approach?
BMM_GENENRIC_TYPE {
base_class: BMM_SIMPLE_CLASS("Container"),
generic_parameters: [ BMM_PARAMETER_TYPE("T") ]
}
This representation came to my mind after seeing the the code below in a previous .bmm schema(I didn’t check the latest) regarding RESOURCE_DESCRIPTION_ITEM.original_resource_uri, which allowed List to have Hash as item_type, even though item type should be unitary in BMM_CONTAINER_TYPE
type_def = <
container_type = <“List”>
type_def = (P_BMM_GENERIC_TYPE) <
root_type = <“Hash”>
generic_parameters = <“String”, “String”>
>
- Question: Is this the intended design? Can container classes be represented interchangeably as a
BMM_CONTAINER_TYPEor aBMM_UNITARY_TYPE(Generic) depending on the context of the usage, and What do you think is the best way to handle the generic class declarations, i.eList<T>: Container<T>in BMM in-memory model.
2. Generic Ancestor Descriptions
In reviewing .bmm schemas, I noticed variations in how generic ancestors are described, some use just ancestors where more complex ones use ancestor_defs, for in some classes like Proper_interval<T>: Interval<T>, it is understandable to drop ancestors since both have generic parameter T:
-
Hash<K, V>: The schema uses
ancestors: <Container, ...>. Does this imply thatHashis effectively inheriting fromContainer<Tuple<K, V>>?
Because I don’t think it is possible to inherit non concrete or non instantable type asContainer<Any>. -
Multiplicity_interval: This inherits from
ancestors: <Proper_interval,...>. Given thatMultiplicity_intervalis “An Interval of Integer, used to represent multiplicity, cardinality and optionality in models” (according to Foundation Types Spec) and Multiplicity_interval itself is simple class so shouldn’t its ancestor be closed, so should the BMM schema explicitly describe the generic specialization, or is the “closed” nature implied by its definition?
3. Defining Override and Redefinition Rules
To support a robust semantic analyzer, that can be of use for later if there’s plans on support extensions of BMM like addition of standard library or custom developed bmm files. I’ve been mapping out the rules for property and routine overrides. I’m leaning towards an Eiffel-inspired approach but wanted to confirm if these align with the openEHR roadmap for BMM extensibility:
-
Properties: Covariant overrides allowed.
-
Statics: Assume no overrides/redefinitions permitted?
-
Routines: Covariant signatures (arguments/return types).
-
Uniform Access (Function to Property): Observed in
DV_QUANTIFIED(magnitude: Function → Real property). Is the “reverse” (Property to Function) strictly disallowed to protect assignment semantics?
according to quoting from Bernard Meyer’s OOSC ch14.6Combined with polymorphism and dynamic binding, such redeclarations of routines into attributes carry the Uniform Access principle to its extreme.
Not the other way around
You might expect to be able to redefine an attribute into an argumentless function. But no.
Assignment, an operation applicable to attributes, makes no sense for functions. -
Diamond/Multiple Inheritance conflicts: in Contrast to Eiffel which it forces you to make an explicit design decision using qualifers like
selectandredefineand other clauses. BMM is simpler, I would imagine that such conflicts are not allowed in the first place? (note: I didn’t see such conflicts in current BMM files to my knowledge).same thing for diamond inheritance, e.g some classes
A,Binherit featurefoofromRootand both refedine it it in a different matter, and then classGrandChildwants to inherit (A, B), in such case which features should be inherited, or is this disallowed ?? -
Implicit inheritance from
Any: since Bmm is a unified type system where every class inherits `Any` (similar idea to Java, Eiffel), some classes can explicitly inherit it like `Ordered` and other that don’t like `DATA_VALUE`(inherits definition class that ultimately doesn’t inherit Any directly).
so even implicit inheritance can accessAnyfeatures likeis_equal(other)but their implementation and the strategy of dispatch is left to implementers. and the problems of diamond problem that arise due to this single Root class, this can be left to implementers decision.Question: are there plans to add redeclaration rules to the specs ?
I apologize if these seem like broad questions—my goal is simply to build a consistent “computational contract” for these models. I’d be interested in hearing others opinions and how they handle these edge cases in their own implementations.