Trinary Values
Sentrie uses trinary logic (also known as three-valued logic), which extends traditional boolean logic with a third value: unknown. This is essential for handling cases where information may be incomplete or unavailable.
Trinary Values
Section titled “Trinary Values”Sentrie supports three trinary values:
true- The condition is definitely truefalse- The condition is definitely falseunknown- The condition’s truth value cannot be determined
Kleene Truth Tables
Section titled “Kleene Truth Tables”Sentrie implements Kleene’s three-valued logic, which provides a consistent way to handle unknown values in logical operations.
Logical AND (and)
Section titled “Logical AND (and)”The and operator follows Kleene’s AND truth table:
| AND | true | false | unknown |
|---|---|---|---|
| true | true | false | unknown |
| false | false | false | false |
| unknown | unknown | false | unknown |
Key behaviors:
true and x=x(true is the identity element)false and x=false(false dominates)unknown and true=unknown(cannot determine if both are true)unknown and false=false(false dominates)unknown and unknown=unknown(cannot determine)
Logical OR (or)
Section titled “Logical OR (or)”The or operator follows Kleene’s OR truth table:
| OR | true | false | unknown |
|---|---|---|---|
| true | true | true | true |
| false | true | false | unknown |
| unknown | true | unknown | unknown |
Key behaviors:
true or x=true(true dominates)false or x=x(false is the identity element)unknown or true=true(true dominates)unknown or false=unknown(cannot determine if either is true)unknown or unknown=unknown(cannot determine)
Logical NOT (not or !)
Section titled “Logical NOT (not or !)”The not operator follows this truth table:
| Input | Output |
|---|---|
| true | false |
| false | true |
| unknown | unknown |
Key behavior:
not unknown=unknown(cannot determine the opposite of unknown)
Trinary Logic Examples
Section titled “Trinary Logic Examples”Basic Trinary Operations
Section titled “Basic Trinary Operations”-- Unknown from undefined field accesslet value = user.nonexistent.fieldlet result = value == "test" -- unknown (operation on unknown)
-- AND with unknownlet a = truelet b = unknownlet result1 = a and b -- unknown
let c = falselet d = unknownlet result2 = c and d -- false (false dominates)
-- OR with unknownlet e = truelet f = unknownlet result3 = e or f -- true (true dominates)
let g = falselet h = unknownlet result4 = g or h -- unknownPractical Use Cases
Section titled “Practical Use Cases”shape User { name!: string email?: string age?: number}
fact user: User
-- Check if user has email (handles unknown gracefully)let has_email: trinary = user.email is defined and user.email is not empty-- Result: true if email exists and is not empty-- false if email is defined but empty-- unknown if email is not defined
-- Age verification with unknown handlinglet can_vote: trinary = user.age is defined ? (user.age >= 18) : unknown-- Result: true if age >= 18-- false if age < 18-- unknown if age is not defined
-- Complex logic with unknown propagationlet is_verified: trinary = user.email is defined and user.email matches "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"-- Result: true if email is defined and valid-- false if email is defined but invalid-- unknown if email is not definedHandling Unknown in Rules
Section titled “Handling Unknown in Rules”shape Account { username!: string email?: string verified: bool}
fact account: Account
-- Rule that handles unknown gracefullyrule can_access = default false when account.email is defined { let email_valid = account.email matches "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" yield account.verified and email_valid}
-- If email is not defined, the 'when' clause prevents evaluation-- If email is defined, the rule evaluates normallyUnknown Propagation
Section titled “Unknown Propagation”-- Unknown propagates through operationslet a = user.nonexistent.field -- undefinedlet b = a + 1 -- undefined (operation on undefined)let c = b > 10 -- undefined (comparison with undefined)let d = c and true -- unknown (AND with undefined)let e = d or false -- unknown (OR with unknown)Non-Trinary Value Interpretation
Section titled “Non-Trinary Value Interpretation”Sentrie can work with various data types, and when a value that isn’t explicitly true, false, or unknown is used in a context that requires a trinary value, the system infers the trinary value based on the following rules:
null/undefined→unknown- Numeric primitives (
int,float,uint, etc.) evaluate tofalsewhen zero,trueotherwise. stringvalues are checked for textual keywords first (see table below), otherwisetruewhen they are non-empty.- Structs, channels, functions, and any other non-
nilvalue evaluate totrue
String coercion
Section titled “String coercion”Before falling back to the “non-empty string” rule, strings are normalized to lowercase and matched against the following keywords:
| Input | Result |
|---|---|
"true", "1", "t" | true |
"false", "0", "f" | false |
"unknown", "-1", "n", "nil", "null", "undefined" | unknown |
| Any other non-empty string | true |
Empty string ("") | false |
Collections
Section titled “Collections”For collections like lists, maps, slices, and arrays, truthiness depends on whether they contain elements. Empty collections are false; non-empty collections are true.
Trinary vs Boolean
Section titled “Trinary vs Boolean”Sentrie does not distinguish between trinary and boolean values:
true(bool) →true(trinary)false(bool) →false(trinary)
Best Practices for Trinary Logic
Section titled “Best Practices for Trinary Logic”- Check for definedness before operations:
-- Good: Check if value is defined firstlet result = user.email is defined ? str.length(user.email) > 0 : false
-- Avoid: Operations on potentially undefined valueslet result = str.length(user.email) > 0 -- unknown if email is undefined- Use
is definedto handle unknown:
-- Explicitly handle unknown caseslet can_proceed = user.age is defined and user.age >= 18- Understand unknown propagation:
-- Unknown propagates through all operationslet value = user.missing.field -- unknownlet result = value + 1 -- unknownlet comparison = result > 10 -- unknown- Use default values in rules:
-- Provide defaults for unknown casesrule can_access = default false when user.role is defined { yield user.role == "admin"}-- Returns false if role is not defined (unknown case)