FHIR Chat · Case Statements require consistent type resolution? · cql

Stream: cql

Topic: Case Statements require consistent type resolution?


view this post on Zulip Michael Riley (Jun 23 2021 at 15:46):

I'm trying to work with the choice data in fhir resources, and would really like to handle with dates or periods across the resources where there is a choice. I have this function.

define function msConvertEffective(medStatement FHIR.MedicationStatement):
  case
    when (medStatement.effective as FHIR.dateTime) is not null then (medStatement.effective as FHIR.dateTime).value
    when (medStatement.effective as FHIR.Period) is not null then ToInterval((medStatement.effective as FHIR.Period))
    else null
  end

But it creates this error message.

>> Error [20:3] Expected an expression of type 'System.DateTime', but found an expression of type 'Interval of System.DateTime'.

And when I switch the order of the two case statements, I find that

Error [20:3] Expected an expression of type 'Interval of System.DateTime', but found an expression of type 'System.DateTime'.

So it seems that whatever the first type resolution is in a switch case statement, all other types must match. I would rather not use the if else statements as they read poorly, and these could scale out to all choice fields in the future rather easily. I'm also unsure as to how I could convert this to work with lists, and not just individual resources.

view this post on Zulip JP (Jun 24 2021 at 19:07):

Hi Michael!

In principle you should be able to do that. In practice the cql-translator isn't quite smart enough to do type inference all the way through conditionals. A work-around is to cast the first then statement to the type you want. Here's an example (also handles a List):

  define function msConvertEffective(medStatments List<FHIR.MedicationStatement>):
    medStatments S
      return
        case
          when (S.effective as FHIR.dateTime) is not null then ((S.effective as FHIR.dateTime).value as Choice<DateTime, Interval<DateTime>>)
          when (S.effective as FHIR.Period) is not null then FHIRHelpers.ToInterval((S.effective as FHIR.Period))
          else null
        end

view this post on Zulip Michael Riley (Jun 24 2021 at 21:55):

thanks JP this is illuminating a lot for me.
What is this line called here? It acts like an unwrapper/iterator.

 medStatments S

view this post on Zulip JP (Jun 24 2021 at 22:07):

That's the syntax for a query in CQL:

<query source> <alias>
medStatments S

https://cql.hl7.org/02-authorsguide.html#queries

Any list can be a query source. You typically see this construct when doing retrieves:

[Encounter: "Inpatient"] E
  where duration in days of E.period >= 120

view this post on Zulip JP (Jun 24 2021 at 22:07):

It's not an iterator or unwrapper per se since CQL is declarative (like SQL) as opposed to imperative (like Java, C, etc) but the outcome in this use case is the same. You're basically saying "for each thing in this List, perform this transformation".

view this post on Zulip Michael Riley (Jun 28 2021 at 14:15):

Ah ok, so all statements should generally start with a query source, and a query does not always have to be a retrieval, but could be an existing definition or a parameter in a function. Thank you!


Last updated: Apr 12 2022 at 19:14 UTC