What is inheritancePrecursor field of BMM_PARAMETER_TYPE supposed to represent?

As it says in the topic: exactly which combination of inheritance and generics this field is supposed to help describe? I read the spec and even Archie code base, but I cannot understand what it is supposed to model.

As far as I can see, this field is never set to anything in Archie. I used a breakpoint and debugged the call to .getClasDefinitions() which I thought would access the setter if the condition modelled by this field every occured in the RM, but the setter was never called.

Reading the spec, it says:
ā€œIf set, is the corresponding generic parameter definition in an ancestor class.ā€
My reading of this is we have a type parameter T in SOME_GENERIC_TYPE which has a ā€˜correspondingā€™ type parameter in some ancestor, except I cannot imagine the scenario in which T is somehow associated to a type parameter of another type in the inheritance hierarchy. I just could not think of how that association may come to be, let alone how it would be expressed in terms of declaration of types. In pseudo sytnax, we can have:

SOME_GENERIC_TYPE<T> extends PARENT_TYPE<G>
SOME_GENERIC_TYPE<T> extends PARENT_TYPE extends ANCESTOR_TYPE<G>

but in all these cases T is a type variable of SOME_GENERIC_TYPE and whatever association it may hold ā€¦ I could not think about.

If weā€™re looking at

SOME_TYPE extends PARENT_TYPE<T> then SOME_TYPE may inherit a field with type T but this is not the scenario that is defined by the spec as far as I can see.

What am I missing here?

Search for BmmParameterTypein Archie. It is first used when converting from ODIN (archie/bmm/src/main/java/org/openehr/bmm/v2/persistence/PBmmGenericParameter.javacreateBmmGenericParameter()).

The spec (ā€œIf set, is the corresponding generic parameter definition in an ancestor class.ā€) might be misleading. Especially the part referring to the ā€œancestor classā€.

BMM_PARAMETER_TYPE is a type of parameters of a generic type (T). The actual type of T is via its property BmmParameterType.conformsToType which is of type BmmEffectiveType ā€œconnectingā€ generic parameters to the BMM type system.

Maybe it should be named BMM_GENERIC_PARAMETER since it doesnā€™t directly specify type (it can even have a name only, without type ā€“ meaning ANY type).

@borut.jures thanks, but Iā€™m not sure I managed to express myself. I agree with what you say about the BMM_PARAMETER_TYPE, but my question is about the specific field of that type. I can see what BMM_PARAMETER_TYPE does, I want to understand what that specific field indicates.

BMM has attributes.
Java classes have properties.
SQL has columns.
Who has fields you are referring to? :wink:

Iā€™m using BmmParameterTypein BmmGenericClass.genericParameters property:

class BmmGenericClass extends BmmClass {
  Map<String, BmmParameterType> genericParameters = <String, BmmParameterType>{};

Iā€™m still not sure if this is the answer to your question. Iā€™m not sure to understand what the question is :innocent:

Iā€™m referrring to this location in the spec: BMM_PARAMETER_TYPE Class and specifically to inheritance_precursor attribute. Thatā€™s it. Thatā€™s my question. What is it for exactly?

I missed the mention of inheritancePrecursor in the title :blush:

I can confirm that Iā€™m not setting its value in my BMM parser either (just like Archie).

Iā€™m using it in only one place ā€“ if conformsToType would be null, then inheritancePrecursor would be used.

However after researching it further, I remembered that when generating code, I have to handle cases where conformsToType of a generic parameter T isnā€™t specified for a BMM type. Since languages like Java/Kotlin/C# require a type, the generator searches for the type of T in ā€œthe corresponding generic parameter definition in an ancestor classā€ ā€“ just like the quoted description from the specs says.

What I do in the generators, should probably be done when parsing BMM files. If this was the case, I would set inheritancePrecursor to the found type of T in the ancestor class.

The example you provided would use T in the PARENT_TYPE (instead of G):

SOME_GENERIC_TYPE<T> extends PARENT_TYPE<T extends BMM_PARAMETER_TYPE>

Maybe my memory of parametric polymorphism in JAVA/C# etc are a bit rusty but I donā€™t think you can declare a type in these mainstream languages so that T in the below declaration are interpreted as the same type parameter for both the child and parent types. I.e. the compiler(s) would interpret these type parameters in the scope of declaring child and parent types. Update: I just reread the declaration and I think Iā€™m wrong: T would be defined as a type parameter during declaration and its scope would include PARENT_TYPEā€™s declaration

SOME_GENERIC_TYPE<T> extends PARENT_TYPE<T extends BMM_PARAMETER_TYPE>

Iā€™m still not clear on the exact case though. @pieterbos care to comment on this one?

Yesterday I watched @thomas.beale add a new abstract type in the middle of class hieararchy and my comment was: ā€œit looks nice in the UML diagram, but it gets tricky when converting the types to classes in programming languagesā€ :wink:

One major obstacle for most languages is when a type of a property is changed in a descendant class. I managed to solve this for 4 programming languages, but Iā€™m always anxious what the next version of BMM files will bring :blush:

Iā€™m also eager to learn the intended way of using the inheritance_precursor attribute.

Using slightly more comprehensible class names, imagine FAST_SORTED_LIST<T> extends SORTED_LIST<T>. The inheritance_precursor of 'T' in an instance of BMM_GENERIC_CLASS is the BMM_PARAMETER_TYPE object representing is the 'T' in the BMM_GENERIC_CLASS instance representing SORTED_LIST<T>. This is replicated for all generic paramaters for which there are precursors in the inheritance hierarchy, e.g. '<K,V>' params of Map types.

Itā€™s useful for each generic parameter in a descendant generic class to know its inheritance precursor, because that may have a type constraint, i.e. to know that the first 'T' below has to conform to ORDERED.

FAST_SORTED_LIST<T> extends SORTED_LIST<T extends ORDERED>

We could do without the stored inheritance_precursor, and compute it each time. Itā€™s in the spec to support implementations that compute it on first read of the classes - itā€™s surprisingly tricky to get right, since the numbers of generic parameters can vary from child to parent etc. To compute it on the fly means doing all this work every time. No-one would notice of course, so it could indeed be purely computed rather than stored in the in-memory model.

In this case, there is no relation between the 'T' mentioned in SOME_GENERIC_TYPE and the G, because PARENT_TYPE has closed the G generic parameter, e.g. FAST_SORTED_PACKET_LIST extends FAST_SORTED_LIST<PACKET>. Here the G parameter has been ā€˜closedā€™, i.e. substituted, in PARENT_TYPE, which is not itself generic.

1 Like

Thanks Tom.

Either you forgot to type something after ā€˜representingā€™ or this is as far as my English gets me. Sorry, could not follow that. Update: re-read this and almost understood but then it did not make sense: should not the precursor be in the FAST_SORTED_LIST 's bmm representation? Based on you also saying:

Inheritance precursor emerges in the context of ā€¦ inheritance :slight_smile: My expected way of seeing this attribute populated in the child classā€™s bmm representation is explained below:

This is more useful for me, thanks. I think the pseudo syntax is not helping in its current form. If we brake it down:
class SORTED_LIST<T extends ORDERED>{....
followed by:
class FAST_SORTED_LIST<T> extends SORTED_LIST<T> {...

then this gives us the case the inheritancePrecursor attribute is useful for. That is, the subtype extends the supertype with its type parameter. However, since the definition of the supertype already has a constraint defined on its type parameter (T extends ORDERED) the type parameter of subtype (FAST_SORTED_LIST) has to respect that constraint, otherwise it would conflict with its supertypeā€™s contract/definition.

This fact would then be indicated via inhertiancePrecursor attribute of parameter T for FAST_SORTED_LIST (via the BmmGenericType instanceā€™s type parameter )

Assuming I got it, the next question is do we have this anywhere in the RM ? Specifically in BMM speak, in any BmmDefinedType ? As I said, I did not see this field materialise to a non-null value in Archie, though I may not have been able to trigger the correct execution path.

Poor formatting on my part - I adjusted my original reply, hopefully it reads more clearly now.

It does - thatā€™s the point. Donā€™t forget, BMMā€™s purpose is to do exactly this kind of type policing. So in this case, the in-memory representation of a model needs to get its hands on generic parameter pre-cursors to inspect for type constraints - just as the Java or C# compiler has to do.

You did :wink:

Pretty much everything in the BMM was created because we needed it. This particular case would be needed for generic types like DV_INTERVAL<V> (see here) and others (I think in Task Planning from memory). In the soon-to-be-published S2 RM, we have a lot more generics, and the situation this construct solves occurs more often.

Aside: when we were building the openEHR RM back in the early 2000s, and OOPLs were still shaking out (you could still read articles back then that mentioned SmallTalk and Eiffel, as well as C++ and the newcomer Java), I guessed that all languages would evolve to support multiple inheritance, since C++ and Eiffel already had it, but hardly any would handle generic types properly (only Eiffel did at the time). Completely wrong guess, as any Java programmer knows - multiple inheritance never went mainstream, but we now have not just generic types but generic methods.

So supporting generic type semantics in a meta-model like BMM is in my view essential to enable models to be written that will conveniently translate to languages like Java, C#, Kotlin, etc.

That was the first type I checked, but unless Iā€™m being extra thick, nothing inherits DV_INTERVAL in the RM.

Going back to my updated comments above: is inheritancePrecursor supposed to be instantiated/expressed in DV_INTERVALā€™s Bmm representation (BmmGenericClass) or its subtypesā€™ Bmm represantations. (though I cannot see any subtypes)

Other way around - DV_INTERVAL<T> inherits Interval<T: Ordered>.

Some other examples:

It should be instantiated on reading a BMM model, i.e. itā€™s present in memory, not in the BMM file (which is an instance of the P_BMM meta-model).

Sorry, as usual, Iā€™m expecting people to understand whatā€™s on my mind without me explaining it. My question was related to something else I tried to articulate above: when the object oriented BMM representation of DV_INTERVAL is build by Archie, Iā€™d expect the BmmGenericClass representing it to have a type parameter object (BmmTypeParameter), which it does, and I also expect that object to have the inheritancePrecursor set. In other words, I expect inheritancePrecursor attribute to be available in the type parameter object of the subtype (DV_INTERVAL) in this subtype-supertype relationship. Iā€™ll double check, but I think Archie is not populating it, so thatā€™s why I was a bit confused.

As I said, Iā€™ll double check and post the screenshots from the debugger.

By the way, once you use generics and inheritance in the object model, thereā€™s no way to avoid this kind of detail in the meta representation. So Iā€™m not disagreeing with the way the way the meta model is designed. You know I had my fair share of assumption about where programming languages and software development would go :slight_smile: Iā€™m just trying to figure out the why and how of BMM a bit better these days.

Got it - Iā€™ll have a look myself later today when I get a minute, Iā€™ve recently been working on that bit of Archie. It may be that it doesnā€™t do that properly, although they have been very careful in their work, so maybe itā€™s just done another way :wink:

A quick inspection shows that BmmParameterType.setInheritancePrecursor() is never called. It should be called from BmmGenericClass.addGenericParameter(), but is not.

My Eiffel implementation, from which the BMM part of Archie is derived has more code.

I suspect that part of BMM in Archie might have been written before I added the extra code in Archie (inspecting dates in Github would reveal things). Generally @pieterbos seems to have either used what I did, or else invented a better replacement. In this case, I canā€™t see any replacement.

So unless it is secretly being done somewhere else that we canā€™t see, we might consider writing a PR to fix that. Iā€™ll have a really good look soon and test if it is working or not with DV_INTERVAL<T>, and if not, add the extra logic.

Unless someone else beats me to it :wink:

1 Like

The BMM implementation was done by Claude Nanjo, and integrated into Archie by me in collaboration with him. This implementation was missing a couple of things. I implemented the parts that were absolutely essential for use in openEHR. Then later we refactored it, replacing the custom ODIN parser code with the generic Archie one and unraveling quite a lot of spaghetti into a more clear control flow. Thomas also did work on more refactoring, and some more unimplemented bits were implemented as part of this process. But some parts of the BMM functionality in Archie remain unimplemented, likely including this.

3 Likes