Operators Reference
Complete guide to all operators in PolicyFlow
PolicyFlow provides a comprehensive set of operators for arithmetic, comparison, logical operations, and more. This reference covers all operators with examples and best practices.
Arithmetic Operators
Basic Arithmetic
Operator | Name | Description | Example |
---|---|---|---|
+ | Plus | Addition / Concatenation | 5 + 3 → 8 |
- | Minus | Subtraction | 10 - 4 → 6 |
* | Star | Multiplication | 6 * 7 → 42 |
/ | Slash | Division | 20 / 4 → 5 |
% | Modulo | Remainder | 17 % 5 → 2 |
Type-Specific Arithmetic
String Concatenation
const greeting = "Hello, " + user.name + "!";
const path = "/users/" + user.id + "/profile";
Temporal Arithmetic
// DateTime + Duration → DateTime
const deadline = DateTime.Now() + Duration.FromUnit(TimeUnit.Days, 7);
// DateTime - Duration → DateTime
const weekAgo = DateTime.Now() - Duration.FromUnit(TimeUnit.Days, 7);
// DateTime - DateTime → Duration
const age = DateTime.Now() - user.birthDate;
const ageInDays = age.TotalDays();
// Duration + Duration → Duration
const totalTime = duration1 + duration2;
Examples
rule CalculateDiscount {
when {
const basePrice = resource.price;
const discountPercent = user.isPremium ? 20 : 5;
const discount = basePrice * discountPercent / 100;
const finalPrice = basePrice - discount;
return finalPrice <= user.budget;
}
then ALLOW
}
rule CheckQuota {
when {
const used = user.storageUsed;
const quota = user.storageQuota;
const percentUsed = (used * 100) / quota;
return percentUsed < 90;
}
then ALLOW
}
Comparison Operators
Equality and Inequality
Operator | Name | Description | Example |
---|---|---|---|
== | Equal | Value equality | user.id == resource.ownerId |
!= | Not Equal | Value inequality | user.status != "suspended" |
Relational Operators
Operator | Name | Description | Example |
---|---|---|---|
< | Less Than | Strictly less | user.age < 18 |
<= | Less Than or Equal | Less or equal | score <= 100 |
> | Greater Than | Strictly greater | balance > 0 |
>= | Greater Than or Equal | Greater or equal | level >= required |
Type Comparisons
Different types have natural ordering:
- Numbers: Mathematical ordering
- Strings: Lexicographical ordering
- DateTimes: Chronological ordering
rule CompareExamples {
when {
// Number comparison
const hasBalance = user.balance > 0;
// String comparison (lexicographical)
const isLaterAlphabet = user.lastName > "M";
// DateTime comparison
const isExpired = resource.expiresAt < DateTime.Now();
// Version comparison using strings
const isNewVersion = resource.version >= "2.0.0";
return hasBalance AND !isExpired;
}
then ALLOW
}
Logical Operators
Boolean Operators
Operator | Alias | Description | Example |
---|---|---|---|
&& | AND | Logical AND | isActive && isVerified |
` | ` | OR | |
! | NOT | Logical NOT | !user.isSuspended |
Short-Circuit Evaluation
Logical operators use short-circuit evaluation:
rule ShortCircuitExample {
when {
// If user is null, the second condition is not evaluated
if (user != null && user.isActive) {
return true;
}
// If first condition is true, second is not evaluated
if (user.isAdmin || expensiveCheck()) {
return true;
}
return false;
}
then ALLOW
}
Complex Boolean Logic
rule ComplexLogic {
when {
const isAuthorized = (user.isActive AND user.emailVerified)
OR user.isAdmin;
const canAccess = isAuthorized
AND (resource.isPublic OR user.id == resource.ownerId)
AND NOT resource.isDeleted;
return canAccess;
}
then ALLOW
}
Null-Safety Operators
Null-Safe Navigation (?.
)
Safely access properties that might be null:
rule NullSafeAccess {
when {
// If user.manager is null, entire expression returns null
const managerName = user.manager?.name;
const managerDept = user.manager?.department?.name;
// Chain multiple null-safe accesses
const grandManagerId = user.manager?.manager?.id;
// Safe with method calls
const domain = user.manager?.email?.Domain();
return managerDept == "Engineering";
}
then ALLOW
}
Null Coalescing (??
)
Provide default values for null:
rule NullDefaults {
when {
// Use default if null
const name = user.displayName ?? "Anonymous";
const dept = user.department ?? "Unassigned";
const level = user.clearanceLevel ?? 0;
// Chain with null-safe navigation
const managerEmail = user.manager?.email ?? "no-manager@company.com";
// Multiple fallbacks
const contactEmail = user.email ?? user.backupEmail ?? "support@company.com";
return level >= resource.requiredLevel;
}
then ALLOW
}
Conditional Operator
Ternary Operator (? :
)
Concise conditional expressions:
rule TernaryExamples {
when {
// Basic ternary
const accessLevel = user.isAdmin ? "full" : "limited";
// Nested ternary (use sparingly)
const priority = resource.isCritical ? "high" :
resource.isImportant ? "medium" : "low";
// With calculations
const timeout = user.isPremium ? 3600 : 1800;
const maxAttempts = user.isTrusted ? 10 : 3;
// In conditions
const canEdit = (user.role == "editor" ? true : user.id == resource.ownerId);
return canEdit;
}
then ALLOW
}
Membership Operator
The in
Operator
Check membership in collections:
rule MembershipChecks {
when {
// Array membership
const hasAdminRole = "admin" in user.roles;
const isWeekend = DateTime.Now().DayOfWeek() in [0, 6];
// String contains (substring check)
const isInternalEmail = "@company.com" in user.email;
// Map key existence
const hasAttribute = "clearanceLevel" in user.attributes;
// Multiple values
const hasAnyRole = user.role in ["admin", "editor", "moderator"];
return hasAdminRole OR hasAnyRole;
}
then ALLOW
}
Operator Precedence
Operators are evaluated in this order (highest to lowest):
- Member Access:
.
,?.
,()
- Unary:
!
,-
(negation) - Multiplicative:
*
,/
,%
- Additive:
+
,-
- Relational:
<
,<=
,>
,>=
,in
- Equality:
==
,!=
- Logical AND:
&&
,AND
- Logical OR:
||
,OR
- Null Coalescing:
??
- Ternary:
? :
Precedence Examples
rule PrecedenceExamples {
when {
// Multiplication before addition
const result1 = 2 + 3 * 4; // 14, not 20
// Comparison before logical
const result2 = 5 > 3 && 2 < 4; // true && true = true
// Use parentheses for clarity
const result3 = (user.score + bonus) * multiplier > threshold;
// Complex expression
const canAccess = user.isActive &&
(user.role == "admin" || user.id == resource.ownerId) &&
resource.status != "archived";
return canAccess;
}
then ALLOW
}
Best Practices
1. Use Parentheses for Clarity
// Clear
const canEdit = (user.isOwner || user.isAdmin) && resource.isEditable;
// Unclear
const canEdit = user.isOwner || user.isAdmin && resource.isEditable;
2. Prefer Named Variables
// Good: Clear intent
const isOwner = user.id == resource.ownerId;
const hasPermission = "edit" in user.permissions;
const isEditable = !resource.isLocked && resource.status == "draft";
when isOwner && hasPermission && isEditable then ALLOW
// Less clear: Everything in one expression
when user.id == resource.ownerId && "edit" in user.permissions && !resource.isLocked && resource.status == "draft" then ALLOW
3. Use Appropriate Operators
// Use null-safe operators when needed
const dept = user.manager?.department ?? "Unknown";
// Use ternary for simple conditions
const limit = user.isPremium ? 1000 : 100;
// Use AND/OR aliases for readability in complex conditions
when user.isActive AND (user.hasSubscription OR user.isTrial) then ALLOW
4. Be Careful with Type Coercion
PolicyFlow is strongly typed and doesn't do implicit conversions:
// This will cause a compile error
when user.age > "18" then ALLOW // Error: comparing Number to String
// Correct
when user.age > 18 then ALLOW