clinical_quality_language icon indicating copy to clipboard operation
clinical_quality_language copied to clipboard

Inconsistent Casting in Unions

Open cmoesel opened this issue 3 years ago • 3 comments

I've discovered inconsistent translation behavior in CQL-to-ELM when a function that returns a list of Any is involved in a union. In short:

If you have CQL that only does ListOfIntegers union ToList('MyString'), then the translator will cast each item in ToList('MyString') to an Integer (resulting in nulls).

If, however, you put a definition that does the reverse order first (e.g., ToList('MyString') union ListOfIntegers) -- and then you do ListOfIntegers union ToList('MyString') -- this time the translator will not attempt the cast at all (in either case).

I think the latter behavior (no cast) is preferred, as ListOfIntegers union ToList('MyString') does not setup any expectation that it should be a singly-typed list -- so a I expect a list of mixed types back.

Specific Details (for Reproducing)

Consider the following CQL:

define function "ToList"(Object "Any"):
  if Object is null then List{} else List{Object}

define Foo:
  Tuple{ Bar: 'Baz', Boo: null as Integer }

define NumsUnionFoo:
  List{1, 2, 3} union ToList(Foo)

When run through CQL-to-ELM, the translator attempts to cast ToList(Foo) to an Integer in NumsUnionFoo:

{
  "name": "NumsUnionFoo",
  "context": "Patient",
  "accessLevel": "Public",
  "expression": {
    "type": "Union",
    "operand": [
      {
        "type": "List",
        "element": [
          {
            "valueType": "{urn:hl7-org:elm-types:r1}Integer",
            "value": "1",
            "type": "Literal"
          },
          {
            "valueType": "{urn:hl7-org:elm-types:r1}Integer",
            "value": "2",
            "type": "Literal"
          },
          {
            "valueType": "{urn:hl7-org:elm-types:r1}Integer",
            "value": "3",
            "type": "Literal"
          }
        ]
      },
      {
        "type": "Query",
        "source": [
          {
            "alias": "X",
            "expression": {
              "name": "ToList",
              "type": "FunctionRef",
              "operand": [
                {
                  "name": "Foo",
                  "type": "ExpressionRef"
                }
              ]
            }
          }
        ],
        "return": {
          "distinct": false,
          "expression": {
            "asType": "{urn:hl7-org:elm-types:r1}Integer",
            "type": "As",
            "operand": {
              "name": "X",
              "type": "AliasRef"
            }
          }
        }
      }
    ]
  }
}

Now simply add an expression with the union in reverse order before NumsUnionFoo:

define function "ToList"(Object "Any"):
  if Object is null then List{} else List{Object}

define Foo:
  Tuple{ Bar: 'Baz', Boo: null as Integer }

define FooUnionNums:
  ToList(Foo) union List{1, 2, 3}

define NumsUnionFoo:
  List{1, 2, 3} union ToList(Foo)

NOW when running it through CQL-to-ELM, the translator does not cast ToList(Foo) to an Integer in NumsUnionFoo:

{
  "name": "NumsUnionFoo",
  "context": "Patient",
  "accessLevel": "Public",
  "expression": {
    "type": "Union",
    "operand": [
      {
        "type": "List",
        "element": [
          {
            "valueType": "{urn:hl7-org:elm-types:r1}Integer",
            "value": "1",
            "type": "Literal"
          },
          {
            "valueType": "{urn:hl7-org:elm-types:r1}Integer",
            "value": "2",
            "type": "Literal"
          },
          {
            "valueType": "{urn:hl7-org:elm-types:r1}Integer",
            "value": "3",
            "type": "Literal"
          }
        ]
      },
      {
        "name": "ToList",
        "type": "FunctionRef",
        "operand": [
          {
            "name": "Foo",
            "type": "ExpressionRef"
          }
        ]
      }
    ]
  }
}

cmoesel avatar Mar 14 '23 21:03 cmoesel

Potentially resolved by #1243. Will retest.

JPercival avatar Oct 19 '23 17:10 JPercival

Not fixed by #1243. :)

JPercival avatar Oct 24 '23 17:10 JPercival

Using the first CQL expression with the latest version of the translator gives me this XML for numsUnionFoo

<def name="NumsUnionFoo" context="Patient" accessLevel="Public">
         <expression xsi:type="Union">
            <operand xsi:type="List">
               <element valueType="t:Integer" value="1" xsi:type="Literal"/>
               <element valueType="t:Integer" value="2" xsi:type="Literal"/>
               <element valueType="t:Integer" value="3" xsi:type="Literal"/>
            </operand>
            <operand xsi:type="Query">
               <source alias="X">
                  <expression name="ToList" xsi:type="FunctionRef">
                     <operand name="Foo" xsi:type="ExpressionRef"/>
                  </expression>
               </source>
               <return distinct="false">
                  <expression asType="t:Integer" xsi:type="As">
                     <operand name="X" xsi:type="AliasRef"/>
                  </expression>
               </return>
            </operand>
         </expression>
      </def>```

Using the second CQL expression I get

<def name="NumsUnionFoo" context="Patient" accessLevel="Public">
         <expression xsi:type="Union">
            <operand xsi:type="List">
               <element valueType="t:Integer" value="1" xsi:type="Literal"/>
               <element valueType="t:Integer" value="2" xsi:type="Literal"/>
               <element valueType="t:Integer" value="3" xsi:type="Literal"/>
            </operand>
            <operand name="ToList" xsi:type="FunctionRef">
               <operand name="Foo" xsi:type="ExpressionRef"/>
            </operand>
         </expression>
      </def>```

I can reproduce this issue and confirm that indeed the translator does not cast ToList(Foo) to an Integer in NumsUnionFoo

ddieppois avatar Oct 27 '23 14:10 ddieppois