Unknown & Three-Valued Logic¶
Why there is a third value¶
Most rule languages only know true and false. Atfinity adds a third value: unknown.
A condition is unknown when Atfinity does not yet have the information it needs to decide it. For example, the condition p.is_pep = true is neither true nor false as long as nobody has answered whether the person is a politically exposed person - it is unknown.
This matters because a rule does not just switch something on or off. Its three possible outcomes drive three different behaviors:
| Result | What Atfinity does |
|---|---|
true |
The rule applies - the document, section or field is required. |
false |
The rule does not apply - the attached item is left out. |
unknown |
The outcome still depends on missing information - Atfinity keeps the item pending and asks for the data it needs. |
So unknown is what makes Atfinity ask the next question: it is the engine's way of saying "I cannot decide this yet, give me more information."
Internally
unknownis represented as a missing/Nonevalue, but conceptually it always means the same thing: not known yet.
The idea: stay decisive, only stay unknown when it matters¶
Atfinity follows Kleene three-valued logic. The guiding principle is:
If the final result is already determined by the values we do know, return that result. Only fall back to
unknownwhen the missing information could still change the outcome.
This keeps rules decisive: a rule resolves to a definite true or false whenever the values we already have settle the matter, instead of staying unknown (and pending) when the missing data could not have changed the result anyway.
A worked example¶
Suppose we already know the person is not a PEP (so p.is_pep = true evaluates to false), but we have not yet asked whether they live in a sanctioned country (unknown).
false and unknown->false. Because anandneeds both sides to be true, a singlefalsealready settles it - the unknown side cannot change a falseand. The rule resolves to a definitefalse, so the document or section it is attached to is left out, instead of staying pending.
The mirror case holds for or:
If we already know the person is a PEP (true), then true or unknown -> true regardless of the country, so the rule applies straight away instead of waiting on the missing answer.
Truth tables¶
and - false wins, because one false is enough to make the whole expression false:
and |
true | false | unknown |
|---|---|---|---|
| true | true | false | unknown |
| false | false | false | false |
| unknown | unknown | false | unknown |
or - true wins, because one true is enough to make the whole expression true:
or |
true | false | unknown |
|---|---|---|---|
| true | true | true | true |
| false | true | false | unknown |
| unknown | true | unknown | unknown |
not:
| value | not |
|---|---|
| true | false |
| false | true |
| unknown | unknown |
Comparisons spread the unknown¶
For comparison operators (=, !=, <, <=, >, >=) there is never enough information to decide an outcome when one side is missing. So if either side of a comparison is unknown, the whole comparison becomes unknown:
If p.age has not been provided, this is unknown (not false) - which is why Atfinity asks for the age instead of silently treating the rule as not applicable.
Checking for unknown on purpose¶
When you specifically want to react to missing information, use the dedicated operators rather than a comparison:
is unknown- true when a value is missing.is known- true when a value is present.
These always return true or false, never unknown.
and then / or else are different on purpose¶
The strict and and or evaluate both sides, so they only stay unknown when the missing side could still change the result.
Their counterparts and then and or else deliberately look at the left side first and only evaluate the right side when the left does not already settle the question. Use them when you do not want to ask the second question before the first has been answered.