Builtin predicates - Logic and control
Predicates in this group are used to structure the flow of execution through a Plang program.
(&&)/2, (||)/2, (=>)/2, (<=>)/2, call/1, call/2, catch/3, (,)/2, (!)/0, commit/0, do, (!)/1, (\+)/1, fail/0, false/0, for, halt/0, halt/1, (->)/2, if, in/2, once/1, repeat/0, switch, throw/1, true/0, try, while
(&&)/2,
(,)/2 - sequential execution of two goals.
- Usage
- Goal1 && Goal2
- (Goal1, Goal2)
- Description
- Executes Goal1 and then executes Goal2 if Goal1 was successful.
- The (&&)/2 form is recommended for use within boolean conditions for if, while, and similar statements. The (,)/2 form usually occurs as a result of parsing the statement sequence { Goal1 ; Goal2 }.
- This predicate is equivalent to fuzzy_and/2 when reasoning about terms that involve fuzzy logic.
- Examples
nonvar(X); integer(X)
(nonvar(X), integer(X))
if (nonvar(X) && integer(X)) { ... }
- Compatibility
- The (,)/2 predicate is compatible with Standard Prolog. The (&&)/2 predicate is an alias for (,)/2.
- See Also
- (||)/2, (=>)/2, (<=>)/2, (!)/1, fuzzy_and/2
(||)/2 - alternative execution of two goals.
- Usage
- Goal1 || Goal2
- Description
- Executes Goal1 and succeeds if Goal1 succeeds. Otherwise executes Goal2.
- If Goal1 or Goal2 involves terms that use fuzzy logic, then the fuzzy confidence value of Goal1 || Goal2 is the same as Goal1 while it succeeds, and then the same as Goal2 once Goal1 fails. Use the fuzzy_or/2 operator instead for calculating the logical fuzzy OR of Goal1 and Goal2.
- Examples
nonvar(X) || integer(X)
if (atom(X) || integer(X)) { ... }
- Compatibility
- Standard Prolog has a (;)/2 predicate that is used for disjunction. That predicate has been omitted from Plang because it conflicts with ";" used as a conjunction operator in statement lists.
- See Also
- (&&)/2, (=>)/2, (<=>)/2, (!)/1, fuzzy_or/2, if
(=>)/2 - logical implication.
- Usage
- Goal1 => Goal2
- Description
- Executes Goal1. If it succeeds, then execute Goal2. If Goal1 fails, then Goal1 => Goal2 succeeds.
- Implication differs from (->)/2 in that (->)/2 will fail if Goal1 fails, whereas (=>)/2 will succeed. The implication operator is intended for use in applications that involve propositional logic.
- Essentially, Goal1 => Goal2 is the same as (Goal1 -> Goal2 || true).
- Examples
A => B
true => true succeeds
true => false fails
false => true succeeds
false => false succeeds
- See Also
- (&&)/2, (||)/2, (<=>)/2, (!)/1, (->)/2
(<=>)/2 - logical equivalence.
- Usage
- Goal1 <=> Goal2
- Description
- Executes Goal1 and Goal2. Succeeds if they both succeed or if they both fail. Fails if one succeeds and the other fails.
- Essentially, Goal1 <=> Goal2 is the same as (Goal1 -> once(Goal2) || ! Goal2).
- Examples
A <=> B
true <=> true succeeds
true <=> false fails
false <=> true fails
false <=> false succeeds
- See Also
- (&&)/2, (||)/2, (=>)/2, (!)/1
call/1 - meta-execution of a goal.
- Usage
- call(Goal)
- Description
- If Goal is a callable term, then execute it as though it had been compiled normally. If Goal is not a callable term, then throw an error as described below.
- The effect of a commit/0 inside Goal is limited to the goal itself and does not affect control flow outside the call/1 term.
- Errors
instantiation_error
- if Goal is a variable.
type_error(callable,
Goal
)
- if Goal is not a variable and not callable.
- Examples
call(fail) fails
X = atom(a); call(X) calls atom(a) and then succeeds
call(X) instantiation_error
call(1.5) type_error(callable, 1.5)
call((atom(a), 1.5)) atom(a) succeeds and then type_error(callable, 1.5)
- Compatibility
- The call/1 predicate is mostly compatible with Standard Prolog. The main departure is that Standard Prolog will scan the entire structure of Goal and throw a
type_error
if some part of it is not callable. The last example above would throw type_error(callable, (atom(a), 1.5))
and not execute atom(a)
in Standard Prolog. Plang implements lazy evaluation of call/1 subgoals, so only the outermost layer of Goal is checked before execution begins.
- See Also
- call/2, commit/0, once/1
call/2 - meta-execution of a goal within the scope of a local database.
- Usage
- call(Goal, Database)
- Description
- If Goal is a callable term, then execute it as though it had been compiled normally. If Goal is not a callable term, then throw an error as described below.
- Goal is executed within a context where Database is set as the current database. Predicates that are called by Goal will be looked up in Database before the global database. Builtin predicates implemented in C will always override the local database; for example, it isn't possible to redefine call/2 within a local database.
- The effect of a commit/0 inside Goal is limited to the goal itself and does not affect control flow outside the call/2 term.
- Errors
instantiation_error
- Database is a variable.
type_error(database, Database)
- Database is not a local database.
instantiation_error
- if Goal is a variable.
type_error(callable,
Goal
)
- if Goal is not a variable and not callable.
- Examples
new_database(DB);
assertz(userdef(a, b, c), DB);
call(userdef(A, B, C), DB);
- See Also
- assertz/2, call/1, commit/0, new_database/1
catch/3,
try - catches an error that was thrown during execution of a goal.
- Usage
- catch(Goal, Pattern, Recovery)
- try { Goal } catch (Pattern1) { Recovery1 } catch (Pattern2) { Recovery2 } ...
- Description
- Executes call(Goal) and succeeds or fails accordingly. If Goal throws an error with throw/1, and the error can be unified with Pattern, then call(Recovery) will be executed. If the error does not unify with Pattern, then the error will continue to be thrown further up the call chain.
- In the case of the try statement, each PatternN is tried in turn until a match is found. The RecoveryN goal for that pattern is then executed. Note that this is not the same as catch(catch(Goal, Pattern1, Recovery1), Pattern2, Recovery2). The catch/3 form may execute Recovery2 if an error is thrown during Recovery1. The try statement form will not.
- Compatibility
- The catch/3 predicate is compatible with Standard Prolog. The try ... catch ... statement is the recommended equivalent in Plang because of its better support for multiple catch blocks.
- See Also
- throw/1, call/1, Formal syntax of try statements
commit/0,
(!)/0 - commits the program to the current choice.
- Usage
- commit
- !
- Description
- The commit/0 predicate always succeeds but also prunes alternative solutions at the next higher goal level. It is most often used in predicates with multiple clauses that match the arguments.
- The following example implements a list membership testing predicate:
is_member(X, [X|_]).
is_member(X, [_|T]) { is_member(X, T); }
An issue with this predicate is that it will keep searching for further instances of X
after finding the first. This may not be what was intended. We can solve this problem by committing to the solution once X
is found: is_member(X, [X|_]) { commit; }
is_member(X, [_|T]) { is_member(X, T); }
Another problem with the original version of the predicate is that if the tail of the list is a variable, it may loop indefinitely. We can solve this by adding another clause that commits and fails if the list is a variable: is_member(X, L) { var(L); commit; fail; }
is_member(X, [X|_]) { commit; }
is_member(X, [_|T]) { is_member(X, T); }
- Examples
commit succeeds
! succeeds
- Compatibility
- The (!)/0 predicate is compatible with Standard Prolog. The new name commit is recommended because it is more obvious as to its function.
- See Also
- call/1
do - repeatedly execute a statement until a condition is false.
- Usage
- do { Statements } (Condition);
- do [UnbindVars] { Statements } (Condition);
- Description
- The do loop evaluates Statements, and then evaluates Condition. If Condition is true, then the loop repeats. If Statements fails, then the loop will fail.
- If UnbindVars is specified, then it contains a list of local variables that will be unbound at the beginning of each loop iteration before Statements is evaluated.
- Examples
X = 1; do { stdout::writeln(X); X ::= X + 1; } while (X <= 10);
X = 1; do [Y] { Y is X * 2; stdout::writeln(Y); X ::= X + 1; } while (X <= 10);
- See Also
- for, while, Formal syntax of do statements
(!)/1,
(\+)/1 - negation by failure.
- Usage
- ! Term
- \+ Term
- Description
- If call(Term) succeeds, then fail; otherwise succeed.
- Note: (!)/1 does not take the current fuzzy confidence value into account, so it is not suitable for use in applications that involve fuzzy logic. Use the fuzzy_not/1 predicate instead.
- Examples
X = a; !(X = b) succeeds with X = a
X = a; !(X = a) fails
\+ fail succeeds
! true fails
- Compatibility
- The (\+)/1 predicate is compatible with Standard Prolog. The new name (!)/1 is the recommended spelling.
- See Also
- (&&)/2, (||)/2, (=>)/2, (<=>)/2, fuzzy_not/1
fail/0,
false/0 - always fail.
- Usage
- fail
- false
- Description
- The fail predicate always fails execution of the current goal.
- Examples
fail fails
false fails
repeat; f(a); fail executes f(a) an infinite number of times
- Compatibility
- The fail/0 predicate is part of Standard Prolog. The false/0 predicate exists as an alias in Plang because it is a more natural opposite to true/0.
- See Also
- true/0
for - loop over the members of a list.
- Usage
- for (Variable in List) Statement
- for [UnbindVars] (Variable in List) Statement
- Description
- The for loop iterates over the members of List, binding Variable to each member in turn and performing Statement. The Variable must have a new name that does not occur previously in the clause.
- If Statement fails for a member of List, then the loop will fail. If Statement succeeds for all members of List, then the loop will succeed.
- If UnbindVars is specified, then it contains a list of local variables that will be unbound at the beginning of each loop iteration. The Variable must not appear in UnbindVars.
- Errors
instantiation_error
- if List is a variable, or the tail of List is a variable.
type_error(list,
List
)
- if List is not a variable or a list, or the tail of List is not a variable or [].
- Examples
for (X in List) { stdout::writeln(X); }
for [Y] (X in List) { Y is X * 2; stdout::writeln(Y); }
- See Also
- do, in/2, while, Formal syntax of for statements
halt/0 - stops execution of the top-level goal.
- Usage
- halt
- Description
- Exits from execution of the top-level goal, returning control back to the system with an exit value of 0.
- It is not possible to trap the halt state with catch/3.
- Examples
halt
catch(halt, X, stdout::writeln('not reached'))
- Compatibility
- Standard Prolog
- See Also
- halt/1
halt/1 - stops execution of the top-level goal with a specific exit value.
- Usage
- halt(ExitValue)
- Description
- Exits from execution of the top-level goal, returning control back to the system with the specified integer ExitValue. The ExitValue may be clamped to a system-specific range to make it suitable for passing back to the host operating system.
- It is not possible to trap the halt state with catch/3 if ExitValue is a valid integer.
- Errors
instantiation_error
- ExitValue is a variable.
type_error(integer, ExitValue)
- ExitValue is not an integer.
- Examples
halt(3)
catch(halt(3), X, stdout::writeln('not reached'))
halt(X) instantiation_error
halt(1.0) type_error(integer, 1.0)
- Compatibility
- Standard Prolog
- See Also
- halt/0
(->)/2,
if - if-then statement.
- Usage
- if (Goal1) Goal2
- if (Goal1) Goal2 else Goal3
- (Goal1 -> Goal2 || true)
- (Goal1 -> Goal2 || Goal3)
- Description
- Goal1 is executed, and if it succeeds then Goal2 is executed. If Goal1 fails, then Goal3 is executed. If Goal3 is omitted, then the statement succeeds if Goal1 fails.
- If the goal has the form (Goal1 -> Goal2) without a Goal3 else goal, then the goal will fail if Goal1 fails. By contrast, if (Goal1) Goal2 will succeed if Goal1 fails.
- Examples
if (A) B; else C;
(A -> B || C)
if (A) B;
(A -> B || true) succeeds if A fails
(A -> B) fails if A fails
- Compatibility
- The (->)/2 predicate is compatible with Standard Prolog. Standard prolog expresses if-then-else as
(A -> B ; C)
which is not supported in Plang. The if statement form is the recommended method to express conditionals in Plang.
- See Also
- (||)/2, (=>)/2, switch, Formal syntax of if statements
in/2 - list membership testing.
- Usage
- Term in List
- Description
- Term in List succeeds multiple times whenever Term unifies with an element of List. Fails at the end of the List, if Term does not unify with any of the elements, or the tail of List is not a list or
[]
.
- Errors
instantiation_error
- List or the tail of List is a variable. This prevents an infinite loop if List is partial.
- Examples
X in [a, b, c] succeeds 3 times for X = a/b/c, then fails
f(X) in [a] fails
X in Y instantiation_error
X in [a|Y] succeeds with X = a, then instantiation_error
- Note: if the in/2 predicate is used within a condition for a do, if, or while statement, then it will succeed only once for the first match. This is because the statement conditions perform a commit/0 after success is detected. This is useful for detecting simple list membership only:
if (f(a) in List) {
...
}
The recommended procedural loop construct for lists is for: for (X in [a, b, c])
stdout::writeln(X);
- See Also
- for
once/1 - executes a goal only once, ignoring subsequent solutions.
- Usage
- once(Goal)
- Description
- Executes call(Goal) and then executes a commit/0 to prune searches for further solutions. In essence, once(Goal) behaves like call(Goal, commit).
- Examples
once((X = a || Y = b)) succeeds with X = a, never performs Y = b
once(fail) fails
- Compatibility
- Standard Prolog
- See Also
- call/1
repeat/0 - succeeds repeatedly and indefinitely.
- Usage
- repeat; ...; fail
- Description
- Repeats the sequence of statements between repeat and fail indefinitely until a commit/0 is encountered.
- Examples
repeat; stdout::writeln("hello"); fail
outputs "hello" indefinitely
repeat; ! succeeds
repeat; !; fail fails
repeat; fail loops indefinitely
repeat; a = b loops indefinitely due to unification failure
- Compatibility
- Standard Prolog
- See Also
- fail/0
switch - switch on a term and choose a matching statement to handle the term.
- Usage
- switch (Term) { case Label1: Statement1; ...; case LabelN: StatementN; default: DefaultStatement; }
- Description
- Finds the first LabelM term in the case list that unifies with Term, and executes the associated StatementM. If none of the case labels match, then executes the DefaultStatement associated with the default label. If there is no default label, then the switch statement fails.
- Multiple case labels can be specified for the same statement with case Label1: case Label2: ... case LabelN: Statement. The default label can be mixed with regular case labels.
- Unlike C/C++, execution does not fall through from StatementM to the following StatementM+1.
- Once a LabelM is found that unifies with Term, the switch statement does an implicit commit/0 to commit the clause to that choice. Backtracking does not select later case labels even if they may have otherwise unified with Term.
- Examples
eval(Term, Answer) {
switch (Term) {
case X + Y: {
eval(X, XAnswer);
eval(Y, YAnswer);
Answer is XAnswer + YAnswer;
}
case X - Y: {
eval(X, XAnswer);
eval(Y, YAnswer);
Answer is XAnswer - YAnswer;
}
case X * Y: {
eval(X, XAnswer);
eval(Y, YAnswer);
Answer is XAnswer * YAnswer;
}
case X / Y: {
eval(X, XAnswer);
eval(Y, YAnswer);
Answer is XAnswer / YAnswer;
}
case -X: {
eval(X, XAnswer);
Answer is -XAnswer;
}
default: {
if (number(Term))
Answer = Term;
else
lookup_variable(Term, Answer);
}
}
}
eval(2 * x + y, Answer)
- See Also
- if, Formal syntax of switch statements
throw/1 - throws an error to an enclosing catch goal.
- Usage
- throw(Term)
- Description
- Throws a freshly renamed version of Term as an error to an enclosing catch/3 goal that matches Term. Plang will backtrack to the matching catch/3 goal and then execute the associated recovery goal.
- Compatibility
- Standard Prolog
- See Also
- catch/3
true/0 - always succeed.
- Usage
- true
- Description
- The true predicate always succeeds execution of the current goal.
- Examples
- Compatibility
- Standard Prolog
- See Also
- fail/0
while - repeatedly execute a statement while a condition is true.
- Usage
- while (Condition) Statement
- while [UnbindVars] (Condition) Statement
- Description
- The while loop evaluates Condition at the beginning of each iteration. If Condition is true then Statement will be executed. Otherwise, the loop terminates. If Statement fails, then the loop will fail.
- If UnbindVars is specified, then it contains a list of local variables that will be unbound at the beginning of each loop iteration before Condition is evaluated.
- Examples
X = 1; while (X <= 10) { stdout::writeln(X); X ::= X + 1; }
X = 1; while [Y] (X <= 10) { Y is X * 2; stdout::writeln(Y); X ::= X + 1; }
- See Also
- do, for, Formal syntax of while statements