Unification (=
)
Unification is Polar’s core matching operation. Two values unify if they are equal or if variables can be consistently bound to make them equal. Lists unify if all corresponding elements unify.
The unification operator (=
) checks if its left and right operands unify; for example, "a" = "a"
, x = "a"
, or ["a", "b"] = [x, "b"]
where the variable x
is either bound to "a"
or unbound.
Note that in the case of String
values, unification is case sensitive, e.g.:
"x"
does not unify with"X"
User{"Alice"}
does not unify withUser{"alice"}
Conjunction (and
)
and
requires both conditions in a rule body to be true.
Disjunction (or
)
or
succeeds if either the left or right operand is true. Any or
expression can be rewritten as multiple rules with the same head but different bodies.
Negation (not
)
not
checks that a specific fact does not exist.
This would look like below:
For example, you might use it to say that a user should only be allowed to perform an action if they are not banned, and that the policy grants them that permission:
- You can negate only a single fact, not a compound expression (like one using
and
oror
), or a fact that refers to another policy rule. - Variables inside a negated fact must also appear in a non-negated fact in the same rule. This ensures Polar knows the set of possible values before evaluating the negation.
NOT EXISTS
SQL subqueries.
Polar supports a wide range of patterns using negation, including common use cases like:
- Checking that a user meets all required conditions (e.g., has every required role)
- Allowing access from a parent resource unless explicitly overridden
List membership (in
)
in
iterates over a list of strings.
x
is unified with each list element in sequence. The right operand must be a list of strings.
In the following example, the variable x
will be bound to "a"
, "b"
, and "c"
, in turn, and then the x = "a"
check will evaluate. This expression will only succeed for the first item in the list, "a"
.
x
is unified with each list element in sequence. The right operand must be a list of strings.
This succeeds only for the first element, "a"
.
The left operand can be a value instead of a variable:
Integer Comparisons
<
, <=
, >
, and >=
compare integer values.
If a fact stores a Unix timestamp:***polar expires_at(File:foo, 1670280790) You can write:
matches
Operator
group
to a Group
type and requires both has_group
and has_role
to succeed with the same Group
value.
Operator Precedence
Polar operators are evaluated from highest precedence to lowest as follows:in, matches
=, <, <=, >, >=
not
and
or
b(user) and c(user)
is evaluated first. It returns false
, but because a(user)
is true
, the entire condition evaluates to true or false or false
, which returns true
.
Use ()
to explicitly control evaluation order.
For instance, in the example above, if you instead wanted to state that e
is true only if at least one of a
and b
is true
, and at least one of c
and d
is true, you could rewrite the rule as follows:
or
expressions before applying and
.