-
Key: DMN12-210
-
Status: closed
-
Source: ACTICO ( Daniel Thanner)
-
Summary:
The DMN specification says about singleton lists:
- Chapter 10.3.1.4 on page 110: "A singleton list is equal to its single item, i.e., [e] = e for all FEEL expressions e."
- Chapter 10.3.2.5 on page 113: “Therefore, any function or operator that expects a list as input but instead receives a non-list semantic domain element e behaves as if it had received [e] as input.”
This works for most expressions and built-in functions, but for list built-in functions and the sort built-in function the behavior is ambiguous. This leads to different possible valid results for a FEEL expression. The problem here is that the further processing of this result (in DMN and/or other FEEL expressions) leads to different behavior/results of subsequent (boxed) expressions.
Examples of ambiguous FEEL expressions:
- distinct values(["a", ["a"]])
- possible results: ["a"] or [["a"]] or ["a", ["a"]]
- union(["a"], [["a"]])
- possible results: ["a"] or ["a", ["a"]]
- index of(["a", ["a"]], "a")
- possible results: [1,2] or [1]
- count([[]])
- possible results: 0 or 1
- list contains(["a", ["b"]], "b")
- possible results: true or false
- min([1],2)
- possible results: [1] or 1
- flatten([“a”, [], [[]]])
- possible results: [“a”] or [“a”, []]
Therefore we think, the specification must be detailed to ensure cross vendor interoperability.
Our proposal for DMN 1.2 is not a change of the existing behavior, but a more detailed description of the expected behavior:
Add the following 5 bullet points to chapter 10.3.4.4 List functions before table 61:
- List built-in functions work on the passed lists as they are. A single element may be converted to a singleton list where the parameter domain requires this. (For example count(), list con-tains(), index of()).
- If a built-in function returns a list, any list item originating from the parameters are pre-served, i.e. singleton lists remain (for example reverse(), sort(), min()).
- To operate on list items, singleton lists may be resolved to their single item (for example min(), and(), mean()).
- If a list built-in function identifies list items, FEEL equality is used (for example index of(), list contains()).
- If FEEL equality matches two or more list elements and the built-in function has to decide which equal element should be returned, the function MUST return the element with the lowest position. (For example distinct values())
Additionally the description of the flatten() built-in function should be changed to:
“flatten nested lists and removes empty and nested empty lists”Additionally it may be helpful to categorize the list built-in functions to the following categories:
- Functions searching for list items:
list contains(), index of() - Functions creating a new list with additional or removed items:
sublist(), append(), concatenate(), insert before(), remove() - Functions changing the list order of items:
reverse(), sort() - Functions operating on list items and calculating a result:
count(), min(), max(), sum(), mean(), and(), or() - Set theory functions:
distinct values(), flatten(), union()
We could add a new column “category” to table 61 or divide table 61 into more tables.Examples for the above FEEL expressions with the new applied specification changes:
- distinct values(["a", ["a"]]) = [“a”]
- union(["a"], [["a"]]) = [“a”]
- index of(["a", ["a"]], "a") = [1, 2]
- count([[]]) = 1
- list contains(["a", ["b"]], "b") = true
- min([1],2) = [1]
- flatten([“a”, [], [[]]]) = [“a”]
Even more exampleslist contains([], []) = false // processes passed list as is. This list is empty and cannot contain items.
list contains([[]], []) = true // note: different result as for []
list contains("a", "a") = true // automatic conversion from first parameter "a" to ["a"]
list contains(["a"], "a") = true
list contains([["a"]], "a") = true // FEEL equality is applied where [e] = e
list contains(["a", "b", []], []) = true
list contains(["a", "b", [[]]], []) = trueindex of([], []) = [] // processes passed list as is. This list is empty and cannot contain items.
index of([[]], []) = [1] // note: different result as for []
index of("a", "a") = [1] // automatic conversion from first parameter "a" to ["a"]
index of(["a"], "a") = [1]
index of([["a"]], "a") = [1] // FEEL equality is applied where [e] = e
index of(["a", "b", []], []) = [3]
index of (["a", "b", [[]]], []) = [3]sublist([],1,1) = null // invalid position
sublist([[]],1,1) = [[]]
sublist("a", 1, 1) = ["a"] // automatic conversion from first parameter "a" to ["a"]
sublist(["a"], 1, 1) = ["a"]
sublist([["a"]], 1, 1) = [["a"]]
sublist(["a", "b", []], 3, 1) = [[]]append([], 1) = [1]
append([[]], 1) = [[], 1]
append("a", 1) = ["a", 1] // automatic conversion from first parameter "a" to ["a"]
append(["a"], 1) = ["a", 1]
append([["a"]], 1) = [["a"], 1]concatenate([], []) = []
concatenate([[]], []) = [[]]
concatenate("a", []) = ["a"] // automatic conversion from first parameter "a" to ["a"]
concatenate(["a"], []) = ["a"]
concatenate([["a"]], []) = [["a"]]insert before([], 1, "a") = null // invalid position
insert before([[]], 1, "a") = ["a", []]
insert before("a", 1, "b") = ["b", "a"] // automatic conversion from first parameter "a" to ["a"]
insert before(["a"], 1, "b") = ["b", "a"]
insert before([["a"]], 1, "b") = ["b", ["a"]]remove([], 1) = null // invalid position
remove([[]], 1) = []
remove("a", 1) = [] // automatic conversion from first parameter "a" to ["a"]
remove(["a"], 1) = []
remove([["a"]], 1) = []sort([[[]], [], ["a"], [["a"]]], function (x,y) count(x) > count(y) ) = [[[]], ["a"], [["a"]], []]
reverse([]) = []
reverse([[]]) = [[]]
reverse("a") = ["a"] // automatic conversion from first parameter "a" to ["a"]
reverse(["a"]) = ["a"]
reverse([["a"]]) = [["a"]]min([1], [2]) = [1] // return list item
max([1], [2]) = [2] // return list item// mean(), sum(), and(), or() do arithmetic operations and therefore do never return a list
count([]) = 0
count([[]]) = 1 // note: different result as for []
count("a") = 1 // automatic conversion from first parameter "a" to ["a"]
count(["a"]) = 1
count([["a"]]) = 1distinct values([[[]], [], "a", ["a"], [["a"]]]) = [[[]], "a"] // uses FEEL equality, preserves items, chooses for equal list items item with lower list position
flatten([[[]], [], "a", ["a"], [["a"]]]) = ["a", "a", "a"] // removes empty lists
union([[], [[]], "a", ["a"], [["a"]]], [[["a"]], ["a"], "a", [[]], []]) = [[], "a"]
// uses distinct values(concatenate()) -
Reported: DMN 1.1 — Thu, 19 Oct 2017 14:27 GMT
-
Disposition: Resolved — DMN 1.2
-
Disposition Summary:
Replace e=[e] with implicit de-listing to avoid argument domain error
See attached word doc
-
Updated: Wed, 3 Oct 2018 14:17 GMT
-
Attachments:
- DMN12-210_proposal_v2.docx 17 kB (application/vnd.openxmlformats-officedocument.wordprocessingml.document)
DMN12 — Expected behavior for list/sort built-in functions in combination with singleton lists
- Key: DMN12-210
- OMG Task Force: Decision Modeling and Notation 1.2 RTF