Best Practices & Style
46.1 Naming & layout
Predicate names describe relations between arguments: list_length/2, parent_child/2, employee_salary/2.
Variables: CamelCase with meaningful names (Xs for lists, X for elements, S0, S1, S for state threading).
Argument order: inputs first, outputs last (and the most discriminating argument first for indexing).
One clause per line block; indent bodies by 4 spaces; put ( Cond -> Then ; Else ) on separate lines with aligned operators.
Group all clauses of a predicate together; base cases first.
46.2 Guidelines
Prefer pure, general code; test with most-general queries.
Use dif/2 and CLP(FD) instead of \=, \+, and is/2 when generality matters.
Use cuts sparingly; prefer if-then-else, once/1, and indexing.
Avoid assert/retract for transient state — thread state through arguments (or DCG semicontext).
Document predicates with PlDoc mode/determinism annotations.
Use must_be/2 to validate inputs at API boundaries.
Fix every singleton warning.
Write plunit tests, including failure and error cases.
Use setup_call_cleanup/3 for resources.
Make termination explicit: base cases, decreasing arguments, tabling for recursion over graphs.
Avoid deep ; nesting — split into helper predicates.
Use modules; export only the public API.
46.3 Common pitfalls
Using = for arithmetic is/2 or #= X is Y + 1 with unbound Y ensure instantiation / use CLP(FD) Left recursion → infinite loop reorder goals or :- table \+ on non-ground goals dif/2, ground first Red cuts breaking generality if-then-else / pure alternatives "abc" type confusion set double_quotes flag explicitly [1, 2] vs [1 | 2] know proper vs improper lists Forgetting the period . every clause ends with . Expecting =< to be <= Prolog uses =< Leftover choice points first-arg indexing, ->, deterministic design findall over side-effecting goals separate generation from side effects