Stream: shorthand
Topic: Logical Models in shorthand
Jose Costa Teixeira (Dec 09 2019 at 15:36):
Here is a first poke to see how would FSH look like for a Logical Model.
Jose Costa Teixeira (Dec 09 2019 at 15:36):
Any ideas? Is this something for a call (I can try to join)?
Chris Moesel (Dec 09 2019 at 15:44):
Hi @Jose Costa Teixeira -- the wiki hides it behind an obscure "show 15 more pages" link, but we do have a very notional logical model syntax documented here: https://github.com/HL7/fhir-shorthand/wiki/5.2-Logical-Models
Chris Moesel (Dec 09 2019 at 15:46):
I don't think we've discussed it w/ the community yet as we had been focusing on the profiling piece. @Mark Kramer -- what do you think about adding this to the agenda for Thursday?
Jose Costa Teixeira (Dec 09 2019 at 15:46):
Thanks. Looks flat'ish - can we put some structure there?
Jose Costa Teixeira (Dec 09 2019 at 15:48):
I mean, how to put structure? is it by doing
Element: parent
...
Element: parent.child
...
Chris Moesel (Dec 09 2019 at 17:21):
I think the way it is currently written up, each "level" of the structure is defined as a separate component. So the way you introduce another level deep is by defining a new complex element with the sub-children. In the example in the doc, this is shown by the Model
called MyClass
, which contains a property that is a Backbone
called bar
, which then has nested Element
s called baz
and qux
.
Chris Moesel (Dec 09 2019 at 17:22):
So you're suggesting that perhaps the syntax should allow that nesting inline in a single element definition? Did I understand correctly?
Jose Costa Teixeira (Dec 09 2019 at 18:53):
Not sure. If the purpose is to make it easier to read, I would expect more something like
--- MyClass: - foo 0..1 Description: "An element" Datatype: string or CodeableConcept - bar 0..* Description: "A backbone element" - baz 0..1 Description: "Element in the backbone" Datatype: Patient or Provider - qux 0..1 Description: "Another element in the backbone" Datatype: Quantity
Jose Costa Teixeira (Dec 09 2019 at 18:53):
Just exploring. I do not know the impact.
Mark Kramer (Dec 12 2019 at 18:31):
@Jose Costa Teixeira I'm open to that suggestion. The proposed syntax that you were looking at was spun off CIMPL, where the emphasis was on reuse of any element, at any level. But I do like the compactness of your approach
Jose Costa Teixeira (Aug 24 2020 at 15:44):
Are there any news on this topic?
Chris Moesel (Aug 24 2020 at 18:32):
Not yet. FSH 1.0.0 is not yet officially out nor is SUSHI 1.0.0. After we get those out the door, we'll start talking about post-1.0.0 features more...
Mark Kramer (Aug 26 2020 at 21:32):
We are on the verge of the STU 1 release. Just one more approval and we're there.
Jose Costa Teixeira (Aug 28 2020 at 14:29):
cc the above syntax: if we don't want to bring indenting, perhaps we can use the "with" syntax - or even a shorter version of it.
Jose Costa Teixeira (Aug 28 2020 at 15:20):
if instead of
* with a:
a1 = x
a2 = y
we have a syntax like
* a:
a1 =
a2 =
(in practice, the with:
is replaced by a :
)
Jose Costa Teixeira (Aug 28 2020 at 15:21):
(not sure how to close the with
- semicolon?).
Anyway, this is just an idea that might be useful for the LMs
Chris Grenz (Feb 09 2021 at 19:43):
@Chris Moesel @Mark Kramer Any updates to logical model support in FSH/SUSHI? Looking thru the docs/source and not able to figure out the current state of support...
Jean Duteau (Feb 09 2021 at 19:45):
it is possible to create logical models in shorthand. but you have to create them as Instances of StructureDefinition. Support for syntax that makes it easier isn't there yet as far as I know. I've attached a sample of one of my logical models. LabelerLogicalModel.fsh
Chris Grenz (Feb 09 2021 at 21:04):
I know during the last connectathon there was some discussion of more "direct" support (e.g. https://github.com/HL7/fhir-shorthand/wiki/5.2-Logical-Models). Any more on that thread?
Chris Moesel (Feb 09 2021 at 21:05):
We started off STU2 work by focusing on soft indexing and parameterized rule sets, but we've just started thinking about logical models and resource definitions again. We have a few different syntax ideas and examples kicking around. I'll post a few snippets here so you can see what we're thinking (and offer any feedback)...
Chris Moesel (Feb 09 2021 at 21:12):
Logical model example using HIV Record as the example.
Key to the grammar:
- Use
+
instead of*
to create a new element + {path} {min}..{max} {type (or type or...)} {short} {definition}
- Use
*
to constrain an existing element (same rules and syntax as profiling) - Use multiple
+
/*
to operate in context of last element path with one fewer+
/*
Chris Moesel (Feb 09 2021 at 21:15):
Or if we don't like the +
, we can introduce a new keyword is
instead. But we do think it is important to be explicit about creating a new element vs constraining an existing element.
image.png
Chris Moesel (Feb 09 2021 at 21:19):
Patient resource
Here are some examples of using a similar grammar to define resources. But note that it get's a lot more unwieldy because resources are very text-heavy. This is only the first few elements of Patient
:
image.png
Chris Moesel (Feb 09 2021 at 21:21):
So... another idea is introducing the concept of TextContent
- a way to outboard your text content to another file, optionally even allowing for multiple languages. In that case we'd have a Patient
resource w/ just the structural rules:
image.png
Chris Moesel (Feb 09 2021 at 21:22):
And another file w/ all the text content:
image.png
Chris Moesel (Feb 09 2021 at 21:24):
We also experimented w/ putting text content in YAML, Java resource bundles (property files), etc. But just re-using the FSH syntax seemed most consistent.
Chris Grenz (Feb 09 2021 at 21:31):
Awesome work @Chris Moesel ! Building a system right now that's going to use a lot of FHIR logical models, and this looks like something that would be absolutely invaluable in that.
Chris Grenz (Feb 09 2021 at 21:31):
Are you working on a public branch here? Has any of this made its way into SUSHI?
Chris Grenz (Feb 09 2021 at 21:32):
May be able to bring some contribution to the effort....
Chris Moesel (Feb 09 2021 at 21:36):
At this point we were just tossing around syntax ideas and experimenting. The experimental files are all local and exist only as text files like I screenshotted above. We have not built any of this into SUSHI or any other tooling -- because it's still very subject to change based on feedback from people like you!
Also, we've been heads down trying to get a GoFSH 1.0 release out the door, so we haven't had the time to focus on this quite yet.
Jose Costa Teixeira (Feb 09 2021 at 23:44):
I think the TextContent is equivalent to the idea of "with: ", right?
Jose Costa Teixeira (Feb 09 2021 at 23:49):
I like the shortness of the first approach and the precision of the second, so, just spitballing:
- I'd like
++
instead of+ +
(with space)? - TextContent feel strange. I'd like more words like
with
,use
...
Jose Costa Teixeira (Feb 09 2021 at 23:56):
My idea would be this:
* Patient.identifier[+]:
** system = $ssnnumbers
** value = "123"
* Patient.identifier[+]:
** type = $hospitalids
** value = "456"
Jose Costa Teixeira (Feb 09 2021 at 23:58):
in this case:
the :
tells the parser: From now on, when you see n+1 stars, prefix that element with the expression between this colon and the star(s) before.
the number of stars tells us how deep we are (this could be nested)
Jose Costa Teixeira (Feb 09 2021 at 23:59):
we can use a reserved keyword such as "with" or "TextContent".. but is it necessary?
Chris Moesel (Feb 10 2021 at 05:54):
The Logical
and Resource
syntaxes use the with
idea, but without the with
(no pun intended). We actually started by using with
but then realized it was completely unnecessary. Whenever you go from n
to n+1
*
s, that's an indicator you're going into the context of the previous path. The with
or :
doesn't seem to buy us much except for extra typing.
As for whether it is **
or * *
(or ++
vs + +
) since FSH is (mostly) whitespace-insensitive, the spaces would be optional. I tried both ways and found that for large complex definitions, my eye could parse the whole thing much easier when I put spaces between the bullets (to effectively double the indent size).
Chris Moesel (Feb 10 2021 at 06:06):
The idea for TextContent
was that it would be just that -- reserved for specifying text content. What makes it different than just doing it in a RuleSet
or inline in the definition is that it is optimized for the common use case that every element (e.g., Patient.identifier
) will have multiple text components within the element (e.g., short
, definition
, requirements
, comment
, etc). In standard syntax you would need to access all those things via caret rules:
* ^title = "Patient"
* ^description = """
Demographics and other administrative information about an individual or animal
receiving care or other health-related services.
"""
* ^publisher = "Health Level Seven International (Patient Administration)"
* ^purpose = "Tracking patient is the center of the healthcare process."
* Patient.identifier
** ^short = "An identifier for this patient"
** ^definition = "An identifier for this patient"
** ^requirements = "Patients are almost always assigned specific numerical identifiers."
* Patient.active
** ^short = "Whether this patient's record is in active use"
** ^definition = """
Whether this patient record is in active use. \nMany systems use this property to mark
as non-current patients, such as those that have not been seen for a period of time based
on an organization's business rules.\n\nIt is often used to filter patient lists to
exclude inactive patients\n\nDeceased patients may also be marked as inactive for the
same reasons, but may be active for some time after death.
"""
** ^comment = """
If a record is inactive, and linked to an active record, then future patient/record
updates should occur on the other patient.
"""
** ^requirements = """
Need to be able to mark a patient record as not to be used because it was created
in error.
"""
** ^meaningWhenMissing = """
This resource is generally assumed to be active if no value is provided for the
active element
"""
** ^sModifierReason = """
This element is labelled as a modifier because it is a status element that can
indicate that a record should not be treated as valid
"""
That's a lot of caret rules. TBH, I don't love caret rules. I prefer to avoid them. In addition, there's not an obvious way to support multiple languages there.
TextContent
on the other hand, avoids carets because within the element you're defining, we can just assume the paths are relative to the ElementDefinition
. It also gives us a place to put the language. The same example as above would be:
TextContent: Patient
Language: en
* title = "Patient"
* description = """
Demographics and other administrative information about an individual or animal
receiving care or other health-related services.
"""
* publisher = "Health Level Seven International (Patient Administration)"
* purpose = "Tracking patient is the center of the healthcare process."
TextContent: Patient.identifier
* short = "An identifier for this patient"
* definition = "An identifier for this patient"
* requirements = "Patients are almost always assigned specific numerical identifiers."
TextContent: Patient.active
* short = "Whether this patient's record is in active use"
* definition = """
Whether this patient record is in active use. \nMany systems use this property to mark
as non-current patients, such as those that have not been seen for a period of time based
on an organization's business rules.\n\nIt is often used to filter patient lists to
exclude inactive patients\n\nDeceased patients may also be marked as inactive for the
same reasons, but may be active for some time after death.
"""
* comment = """
If a record is inactive, and linked to an active record, then future patient/record
updates should occur on the other patient.
"""
* requirements = """
Need to be able to mark a patient record as not to be used because it was created
in error.
"""
* meaningWhenMissing = """
This resource is generally assumed to be active if no value is provided for the
active element
"""
* isModifierReason = """
This element is labelled as a modifier because it is a status element that can
indicate that a record should not be treated as valid
Personally I just find that cleaner, but it is a matter of preference. Anyway, I just wanted to clarify what I was going for with TextContent
. Of course if nobody gets it then that will be a good sign that it's probably not the right approach.
Jose Costa Teixeira (Feb 10 2021 at 07:41):
Chris Moesel said:
The
Logical
andResource
syntaxes use thewith
idea, but without thewith
(no pun intended). We actually started by usingwith
but then realized it was completely unnecessary. Whenever you go fromn
ton+1
*
s, that's an indicator you're going into the context of the previous path. Thewith
or:
doesn't seem to buy us much except for extra typing.As for whether it is
**
or* *
(or++
vs+ +
) since FSH is (mostly) whitespace-insensitive, the spaces would be optional. I tried both ways and found that for large complex definitions, my eye could parse the whole thing much easier when I put spaces between the bullets (to effectively double the indent size).
Agree. I also think the with
is optional
And I thought "TextContent" would be a bizarre keyword for "with", and simply use the colon would be better.
Jose Costa Teixeira (Feb 10 2021 at 07:43):
My idea for the colon is to make sure that the **s are not referring to the parent elementdefinition, but to whatever is in between the *
and the :
it could be a slice, for example.
Chris Moesel (Feb 10 2021 at 15:58):
OK. The TextContent
really is an entirely different concept than with (or nested contexts) -- it just happens to use nested contexts as part of its grammar. But they're not the point of TextContent
.
As for the **
, I interpret that to mean in the context of the last path -- which may be something simple, like referenceRange
, a slice, like component[systolic]
, an extension, like extension[http://foo.org/bar-ext]
, a choice, like valueQuantity
, or a nested combination of these, like component[systolic].extension[http://foo.org/bar-ext].valueQuantity
. In short, whatever the previous path was, that is the context inherited by **
. I still don't think :
is necessary.
Jose Costa Teixeira (Feb 10 2021 at 16:12):
so whatever is after the * before the **s, right?
Chris Moesel (Feb 10 2021 at 16:12):
Yeah.
Jose Costa Teixeira (Feb 10 2021 at 16:13):
i was thinking that the colon could be interesting to tell the compiler why this is not a finished sentence
Jose Costa Teixeira (Feb 10 2021 at 16:15):
(and visually it feels good - but I was a Pascal guy, so I may be biased to appreciate the begins and ends of sentences)
Chris Moesel (Feb 10 2021 at 16:34):
OK. I get that. The rule with just a path and no other action can seem a little odd to someone who's used to always seeing something after the path. I guess I'd ask if that is the _only_ way you can establish a context? Or can you establish a context under a rule that also does other things. For example, do you think this should be allowed?
Chris Moesel (Feb 10 2021 at 16:34):
* identifier 1..1 MS
** system = "http://foo.org/mysystem"
Elliot Silver (Feb 10 2021 at 16:35):
I think a problem with the : syntax is that there is no way to mark the "end of the sentence." Or at least not in a way that provides a benefit over just using *'s.
Elliot Silver (Feb 10 2021 at 16:36):
Is this general idea also being considered for Questionnaire?
Chris Moesel (Feb 10 2021 at 16:37):
Right. From a parsing perspective, the *
is all I need in order to know that the previous "sentence" is done. I don't really need the :
.
Chris Moesel (Feb 10 2021 at 16:39):
Elliot Silver said:
Is this general idea also being considered for Questionnaire?
The notion of contexts is actually separate from logical models, but logical models really highlight why such a thing is useful. So, yes, this notion of contexts with **
and ***
, etc, would apply throughout most of the shorthand language. In profile definitions, extension definitions, instances (including Questionnaire), etc. You could always use **
(or * *
) to work within the context of the last stated path.
Chris Moesel (Feb 10 2021 at 16:42):
We do need to figure out if/how it interacts w/ RuleSets though. For example, if I do ** insert MyRuleSet
, should all paths in the ruleset be interpreted at the inherited context? In some cases, that would be quite nice, but maybe there are others where it would be confusing. We need to work that out some more I think.
Chris Moesel (Feb 10 2021 at 16:46):
We'd love more feedback on the proposed logical model approach (and context too) so we can nail it down before we beginning development (or taking up others' offers to help with development!). @Ward Weistra, I know your crew has some experience with logical models. Any feedback? @Jean Duteau -- you've been a FSH advocate and power user from the start. Any thoughts? Same for @Kurt Allen, @David Hay, and many others... Appreciate your thoughts. Our first starts at a logical model and/or resource syntax introduced halfway through the thread here.
Elliot Silver (Feb 10 2021 at 17:14):
I don't object to the idea of TextContent (ugh, I keep reading/typing Context), but...
I like that it allows you to split content into multiple files and define a context for that file. That's a positive, but we already could split content into multiple files, e.g. using rulesets, so the real main advantage is the context.
On the other hand, what makes the text elements special? I think in part, you've eliminated the need for ^ syntax for most common uses with custom syntax for cardinality, must support, binding, etc. But, how do I set the item's ElementDefinition.code, or alias, or any of the other less common definitional elements? I think I still need to drop down to ^ syntax. Perhaps what is needed is the ability to use any element without using ^ syntax (based on one of your examples above):
+ communication is 0..* BackboneElement
* * obeys ele-1
+ + language is 1..1 CodeableConcept
* * * obeys ele-1
* * * from LANGUAGES (preferred)
* * * code = $LNC#1234
* * * short = "my short description"
* * * example[+]
* * * * label = "this is an example"
* * * * valueDate = '2021-01-01'
The outstanding capability is to be able to specify the language(s).
It would be nice to clarify if there is a difference between + +
and * +
. Are both valid? Are do they mean the same?
David Hay (Feb 10 2021 at 17:43):
I quite like the idea of TextContent. In the clinFHIR modeller I've found that the textual stuff is important - especially when using the model to capture input from domain experts - but inserting it in the main model would certainly obfsucate the model definition (IMHO)
Jose Costa Teixeira (Feb 10 2021 at 17:48):
Chris Moesel said:
* identifier 1..1 MS ** system = "http://foo.org/mysystem"
Indeed, I don't like this (atm I cannot explain why)
Jose Costa Teixeira (Feb 10 2021 at 17:51):
Chris Moesel said:
We do need to figure out if/how it interacts w/ RuleSets though. For example, if I do
** insert MyRuleSet
, should all paths in the ruleset be interpreted at the inherited context? In some cases, that would be quite nice, but maybe there are others where it would be confusing. We need to work that out some more I think.
this discovery is fundamental, I think.
- I think
**
insert MyRuleSet should indeed apply to the context determined by the**
David Hay (Feb 10 2021 at 17:51):
of the examples thus far, I found the HIV one (with + symbols) the easiest to grok...
David Hay (Feb 10 2021 at 17:54):
How would you represent elements with different possible datatypes? like Patient.multipleBirth[x] ?
Chris Moesel (Feb 10 2021 at 18:33):
David Hay said:
How would you represent elements with different possible datatypes? like Patient.multipleBirth[x] ?
Similar to how you do it in an only
rule: by using or
:
+ multipleBirth[x] 0..1 boolean or integer "Whether patient is part of a multiple birth"
Chris Moesel (Feb 10 2021 at 18:44):
@Elliot Silver -- I think we need the ^
to indicate if you're talking about a property from the ElementDefinition
or a sub-path of the element in context. E.g., a shortened (And slightly modified) version of your example:
+ communication is 0..* BackboneElement
+ + language is 1..1 CodeableConcept
* * * coding = $LNC#1234
* * * short = "my short description"
In the case above:
coding
refers tocommunication.language.coding
(thecoding
sub-path of thecommunication.language
CodeableConcept)short
refers tocommunication.language ^short
(theshort
property in thecommunication.language
ElementDefinition)
There is no syntactic hint for which is which. You need to be familiar with the structure of CodeableConcept
and the structure of ElementDefinition
to ascertain what is going on.
There is also potential for ambiguity. How should I interpret this in the context of an Observation profile?
* component
* * code = LOINC#1234-5
Is that a pattern[x] assignment on component.code
or is that setting component ^code
(e.g., ElementDefintion.code
for the component
element?
Chris Moesel (Feb 10 2021 at 18:47):
It would be nice to clarify if there is a difference between + + and * +. Are both valid? Are do they mean the same?
Good question. Discussing this with @Mark Kramer, he thought that the bullet style should be consistent on a given line. So we're going in with the idea that it's either * *
or + +
, but never * +
or + *
. This approach probably is best at eliminating any ambiguity, but it (and all of this) is certainly up for discussion.
Elliot Silver (Feb 10 2021 at 19:24):
Chris Moesel [said](https://chat.fhir.org/#narrow/stream/215610-
In the case above:
* `coding` refers to `communication.language.coding` (the `coding` sub-path of the `communication.language` CodeableConcept)
* `short` refers to `communication.language ^short` (the `short` property in the `communication.language` ElementDefinition)
Perhaps this is a place where "with" or equivalent is needed. E.g.
+ communication is 0..* BackboneElement
+ + language is 1..1 CodeableConcept with:
* * * short = "my short description"
* * * endwith
* * * coding = $LNC#1234
I think the key is that there are two "modes" (although I'm having trouble articulating what those two are; describing the element attributes, and ...?) You've eliminated the many places where the element attributes are described by defining special syntax: obeys, is, etc. and thus you assume any other content is the (?) use. Currently to get back to the element attribute you need to use ^ syntax.
Ward Weistra (Feb 11 2021 at 13:43):
- Like the ability to separate out text labels to a separate file, especially if you can start using those for different languages (
nl-NL
,en-US
) - Would definitely directly look at ability to define Custom Resources and editing the core resources while you're at Logical Models, which you already seem to be doing.
Mark Kramer (Feb 11 2021 at 22:08):
I just think of trying to explain that to someone who is new to FSH and blowing their minds. Keeping it simple is a VERY important goal here.
Jose Costa Teixeira (Feb 12 2021 at 00:18):
I agree, and I think consistency will address that. The above discussion is not simple, but the solutions seems reasonably simple.
Jose Costa Teixeira (Feb 12 2021 at 00:20):
I presume the same way to do a LM will be the same to create a custom resource thus addressing @Ward Weistra's request
(The syntax above just helps to simplify the nesting)
Jose Costa Teixeira (Feb 12 2021 at 00:21):
I also struggle with the caret topic that Elliot and Chris are bringing up
Chris Moesel (Feb 12 2021 at 13:37):
Mark and I discussed the concept of TextContent
a little more, and given that it does seem to have caused some confusion, we think maybe it goes to the back-burner for now. If you want to separate out text from structure, that is possible using RuleSets. E.g.:
Resource: Patient
Parent: DomainResource
* insert PatientText
+ identifier is 0..* SU Identifier
// etc.
RuleSet: PatientText
* ^title = "Patient"
* ^description = """
Demographics and other administrative information about an individual or animal
receiving care or other health-related services.
"""
* ^purpose = "Tracking patient is the center of the healthcare process."
* identifier
* * ^short = "An identifier for this patient"
* * ^definition = "An identifier for this patient"
* * ^requirements = "Patients are almost always assigned specific numerical identifiers."
Then we're not introducing as many ideas at once. And maybe the existing mechanims work just fine. We shall see.
Elliot Silver (Feb 12 2021 at 18:05):
I agree with @Mark Kramer about keeping it simple. I think the debate was about what is "simple." :wink:
I also think there is some value to easily being able to do multi-language content, or pass narrative off to someone different than the person writing the technical content, so I don't want to dismiss TextContent, but it does seem rather arbitrary which elements are supported. Would it only be a set of predefined elements, or any string, markdown, html element?
Mark Kramer (Feb 14 2021 at 17:05):
For TextContent, I think a lot can be done without special constructs using RuleSet. There are several possible approaches to factor the text content into a different file or files using that approach.
Martin Höcker (Feb 15 2021 at 21:00):
Thank you @Chris Moesel for showing the example of how to separate text from the rest of the structure using a RuleSet! This is solving a problem I was facing. I also really liked the syntactic sugar of your TextContent-proposal. It looks a lot cleaner. Texts and translation are often captured and edited by domain experts and/or translation specialists, and I'm sure they would greatly appreciate the visual simplicity of TextContent. I understand this is not a priority right now, but I would certainly appreciate seeing it implemented sometime!
Regarding the new syntax to distinguish between creating elements and constraining elements: Might we be able to get by without this per-rule distinction? In my (limited) understanding, one either creates a new resource-type using a StructureDefinition with .derivation = #specialization, or one would constrain a resource-type/data-type using a StructureDefinition with .derivation = #constraint. Assuming that specialization and constraint are never mixed, then the new "Resource"-keyword you proposed would be distinctive enough for me.
Chris Moesel (Feb 15 2021 at 21:45):
Hi @Martin Höcker -- thanks for the feedback and glad we could provide an approach that might be useful today!
As for create vs constrain -- you're right that in general a specialization
will create and a constraint
will constrain, but I was thinking that within a logical model, you might need to further constrain one of the elements you created (like binding to a VS) -- and I wanted to make those clear so that if you had a typo (desgnation
vs designation
), it would be obvious that you weren't trying to create a new thing called desgnation
.
But... as I think it through, I guess that if we only create using a rule of the form {path} {card} {optional-flags} {type}
, then any other form of rule is by-definition a constraint. So maybe the form of the rule itself is enough to indicate it is creating something... I think you may be on to something -- but I'll have to think it through some more. Thanks!
Last updated: Apr 12 2022 at 19:14 UTC