Constraints on class methods

When I was trying to validate an archetype with the reference model
(openEHR-EHR-OBSERVATION.apgar), I found something strange on all
'event' archetypes.
The EVENT class has a function (method) that calculates the offset.
However, in that archetype the offset was restricted as if it was an
attribute.
What is the sense for this? Shouldn't this restriction be expressed as
an assertion over the different attributes? (that is, a restriction
that must be checked at runtime as it is a result from a method).

In ADL/AOM, constraints can be made on computed properties as well as
stored ones. If you look at the spec, EVENT.offset is computed as
time.diff(parent.origin). Making a constraint on EVENT.time, which is
the absolute time (which is what you want in the data) is annoying
because you want to constraint on relative time, not absolute time. It
would mean creating constraints in the rules section with an equivalent
expression, i.e.

.../some/path/to/event[5 min]/time - .../some/path/to/event[5
min]/parent/origin <= PT5m

- thomas

Oh, this is the first time I have heard that functions can be
constrained. However, AOM specifications say otherwise:
"C_attribute: a node representing a constraint on an attribute
(i.e. UML ‘relationship’ or
‘primitive attribute’) in an object type;" (AOM specifications, page 12)
This excludes clearly the 'stored properties'.

Thomas, Rong and I had a similar discussion many moons ago, and in the end I think we agreed to disagree :wink:

A few other functional properties come to mind such as “type” in PARTY_RELATIONSHIP and “is_integral” in DV_QUANTITY.
These are more or less frequently constrained in archetypes as well.

At the very least my thinking is that it is counter-intuitive to constrain them directly.
Also, for the Java validator that is also used in CKM, we had to introduce a special hard-coded check for “commonly constrained functional properties” such as the three mentioned here.

Re “offset”:
I wonder if offset could be expressed as an attribute with an invariant for its validity.

In fact, looking at the specs, there already is an invariant “Offset_validity”:
inv: offset <> Void and offset = time - parent.origin

Now I wonder what the point of this invariant is when offset is a function that is already defined to be “time - parent.origin”?

Re “type”: This is the same as the property “name” (because of the type_validity invariant)

Sebastian

(attachments)

oceanlogo.png

That wording in the current release does exclude non-attributes. But I
think it should be possible to state constraints on computed attributes
(public functions with no parameters). It has already been implemented
and is not difficult.

- thomas

Sebastian Garde wrote:

A few other functional properties come to mind such as "type" in PARTY_RELATIONSHIP ...
Re "type": This is the same as the property "name" (because of the type_validity invariant)

Yes, funny you should mention that, Sebastian, because I discovered yesterday that this is a bug in the spec. As is well known, the "name" must be unique among siblings within a container. This uniqueness is incompatible with the PARTY_RELATIONSHIP "type", because it would be common for a party to have multiple relationships of the same type.

http://www.openehr.org/issues/browse/SPECPR-54 discusses this. I had to find a work around for it in my software. I chose to violate the type_validity invariant: when setting the type, I append a sequential number to it to set the name; and I compute the type by stripping the sequential number off the name. This ensures that the name is unique, while permitting multiple siblings of the same type.

- Peter

Doesn’t this create problems while using archetypes/templates as basis for the generation of data instances?

I mean, a computed attribute (for example, the EVENT offset) gets its value from already existing values or attributes of the instance class (in this example, from the time and the parent.origin). We should not create the instance if data is not valid regarding the archetype/template, but we cannot check the validity of the constrained offset while we don’t have the instance complete. It seems somehow a vicious circle. An assertion here is clearly preferable, since by definition it is only applied to existing instances.

David

2012/1/10 Thomas Beale <thomas.beale@oceaninformatics.com>

Thomas, Rong and I had a similar discussion many moons ago, and in the end I think we agreed to disagree :wink:

A few other functional properties come to mind such as “type” in PARTY_RELATIONSHIP and “is_integral” in DV_QUANTITY.
These are more or less frequently constrained in archetypes as well.

At the very least my thinking is that it is counter-intuitive to constrain them directly.
Also, for the Java validator that is also used in CKM, we had to introduce a special hard-coded check for “commonly constrained functional properties” such as the three mentioned here.

I am a bit surprised by that, because they can be represented easily in a standard way. Have a look at this schema and search for ‘is_computed’.

Re “offset”:
I wonder if offset could be expressed as an attribute with an invariant for its validity.

well then it would be redundant with the ‘time’ attribute of EVENT.

In fact, looking at the specs, there already is an invariant “Offset_validity”:
inv: offset <> Void and offset = time - parent.origin

Now I wonder what the point of this invariant is when offset is a function that is already defined to be “time - parent.origin”?

this is what defines it. Arguably it should have been a post-condition of the function rather than a class invariant. But the effect is essentially the same.

  • thomas

David,

the usual situation in operational systems is that there are RM objects
being created by some process. These will not by default obey the
template and its archetypes, only the RM; to make the instances obey the
template, you have to do something specific, e.g. engineer (or generate)
the UI so it only allows exactly what the template says, or... if you
have a custom UI (still the case in most real systems today) you will
make calls to some programming object to either set or check the data.
If you use the 'Template Data Object' approach - an API generated from
the template, various types of checking are done. Usually, the checks
are done after a 'commit' call is made, and any wrongly set fields have
to be fixed by making a new call with appropriate data. This is a
similar to the process of the typical web-page on a booking site, where
you can't get to the next page until there are no more 'red' fields to
correct.

There are a lot of different ways to technically do this data setting,
too many to explain here, but in essence, a RM-valid but
template-invalid RM instance is always possible in the instance building
phase; what can't happen in a proper system is for non
template-compliant data to be committed to the EHR repository.

- thomas

If you still say that properties can be restricted, then current
stable validated bmm files are incorrect, as they are currently
missing 90% of stored properties (all methods without parameters),
like all the ones in ITEM_TABLE.

Thomas, Rong and I had a similar discussion many moons ago, and in the end I think we agreed to disagree :wink:

A few other functional properties come to mind such as “type” in PARTY_RELATIONSHIP and “is_integral” in DV_QUANTITY.
These are more or less frequently constrained in archetypes as well.

At the very least my thinking is that it is counter-intuitive to constrain them directly.
Also, for the Java validator that is also used in CKM, we had to introduce a special hard-coded check for “commonly constrained functional properties” such as the three mentioned here.

I am a bit surprised by that, because they can be represented easily in a standard way. Have a look at this schema and search for ‘is_computed’.

Sure - that’s the sound approach if you need all functional properties.
We just needed these three, because these seem to be the only ones that are a) functional and b) commonly constrained. I have never seen another functional property constrained so far. With Peter’s input, it is then probably only 2 (because ‘type’ needs to be revisited anyway).

In our case, we didn’t have to care about computed properties at all - or so we naively thought until we discovered these three!
Until then we thought, that if a property is_computed, then we can actually compute it and don’t need anything else…

Re “offset”:
I wonder if offset could be expressed as an attribute with an invariant for its validity.

well then it would be redundant with the ‘time’ attribute of EVENT.

Yes. I think the real reason why we are having this discussion is that we two different viewpoints here: Yours is coming from data-instance of an archetype point of view, whereas mine is coming from an archetype constraining point of view.
In my view, having this redundancy would be better/clearer/more maintainable than having to constrain functional properties when creating archetypes.
Your view, that in the data instance of the archetype, the actual time should be directly recorded (and not calculated via origin + offset) is valid of course.

I guess the choice is do yo want computable items clear and tidy OR no redundancy in the attributes??

In fact, looking at the specs, there already is an invariant “Offset_validity”:
inv: offset <> Void and offset = time - parent.origin

Now I wonder what the point of this invariant is when offset is a function that is already defined to be “time - parent.origin”?

this is what defines it. Arguably it should have been a post-condition of the function rather than a class invariant. But the effect is essentially the same.

What you are doing in this whole scenario is probably 100% correct, feasible, etc.
On the other hand, it seems that it is a bit counter-intuitive for people like me who work more in the archetype creation world, rather the data instance of an archetype world, see above.

Sebastian

Hi all,

I also think constraining functions, i.e. computed attributes, in the
reference model classes can be counter-intuitive and probably
unnecessarily complex.

In clinical modelling space, a function is supposedly less obvious to
modeler than an ordinary RM class attribute. Since the result of the
function is derived from several member attributes and the algorithm
is documented in the RM specs, it's reasonable to assume that a
constraint expressed on derived values is less understood than that on
an attribute. If a derived value is so commonly used and indeed
necessary in archetyping, why not make it an attribute in the first
place.

In the engineering space, constraints on static attributes seem to be
more straight-forward to work with. Default data instance can be
safely generated by the combined definitions of archetypes/templates.
Upon saving the data instance, the invariants, or rules in the latest
specs, would be used against the instance for final validation. Now
considering how to handle the constraints on functions, they can't
really be used for instance generation since they are dependent on
other attributes. So they would probably only be used in the final
validation just as rules/invariants. So indeed, why not implement
these function constraints as invariants/rules to start with?

In summary, I would vote for only allowing constraints on ordinary,
static attributes. Use invariants or rules to achieve same effects as
constraints on functions.

Cheers,
Rong

Peter,
I believe this unique name rule should be reviewed and revoked. It is not formally defined, as indicated in your referenced Jira issue its only stated in the architecture overview in the context of paths which assumes name is the unique within a container. I have other examples where it is desirable to get multiple items with the same node-id but not the entire set and name is the obvious collector. It also causes issues in renamed templated items which you still want to allow more than one occurrence of that item.
I believe that path predicates are context specific, some times you may want to use event time or entry uid for example, and should not be dictated by the reference model.
Heath

Further to my previous email, I believe the original intent of the name attribute is a form caption of an element value, the approach of adding a numeric suffix to provide a unique key is contrary to this original intent.
Btw, another example of multiple names values conflict with this unique name rule is multiple alias party-identity occurences, in fact anywhere where you use a coded name such as a lab observation with multiple occurrences. Adding a suffix makes the value different to the code rubric, which frowned upon in terminology circles.

Heath

Heath Frankel wrote:

I believe this unique name rule should be reviewed and revoked. It is not formally defined, as indicated in [ http://www.openehr.org/issues/browse/SPECPR-54 ] its only stated in the architecture overview in the context of paths which assumes name is the unique within a container. I have other examples where it is desirable to get multiple items with the same node-id but not the entire set and name is the obvious collector. It also causes issues in renamed templated items which you still want to allow more than one occurrence of that item.

I certainly agree with all of that Heath, having been frequently frustrated myself by this unique name rule.

I thought that ADL 1.5 had resolved this issue a couple of years ago, hadn't it? We're still using ADL 1.4, though, so we are still stuck with the old rule, and I can't remember what the resolution was.

If ADL 1.5 does revoke the unique name rule, then PARTY_RELATIONSHIP's type = name constraint could stay, in some form, depending on the outcome of this discussion about constraints on functions.

- Peter

yep. This rule needs to be relaxed in the next release of openEHR.

- thomas

Further to my previous email, I believe the original intent of the
name attribute is a form caption of an element value, the approach of
adding a numeric suffix to provide a unique key is contrary to this
original intent.

this is correct. The rule for name being set to a string + "_N" was for
the situation where no name value was supplied by user or software - and
- a general assumption that the name often won't be displayed at all,
e.g. if multiple values are simply displayed in some kind of table control.

I am not saying this is good design; just that this was the approach up
to the current release.

Btw, another example of multiple names values conflict with this
unique name rule is multiple alias party-identity occurences, in fact
anywhere where you use a coded name such as a lab observation with
multiple occurrences. Adding a suffix makes the value different to
the code rubric, which frowned upon in terminology circles.

only if the name was a DV_CODED_TEXT, but it would have to be a DV_TEXT
in this case, with the value derived from the code from
LOCATABLE.archetype_node_id (i.e. the meaning of the node).

-thomas

Life would have been much easier if Event recorded 'offset' as a stored
value, and the 'time' was the property being computed. I argued
strenuously for that years ago, precisely to avoid the problem we are
talking about here, but lost the battle :wink:

A nicer approach could be as follows:

  • we allow ‘computed’ attributes in the RM definition used by archetype tools, which will allow archetypes to be very clear and nice, e.g. in the case of ‘offset’

  • in the RM definition, we include a rule that defines the equivalence of such computed attributes to static attributes - i.e. an expression

  • when generating performing the flattening operation, and generating the Operational Template, we replace constraints on computed attributes with the relevant equivalent expressions

  • thomas

Or simply using an assertion on the first place?