Variables and Types in Prolog
Explore atoms, numbers, variables, and terms in Prolog, where unification replaces assignment and types describe the shape of logical data
Prolog’s data model is radically different from mainstream languages. There are no typed declarations, no assignment statements, and no primitive/object divide. Instead, everything is a term, and a “variable” is not a storage location but a placeholder that the Prolog engine tries to bind through unification.
As a logic programming language, Prolog is dynamically typed and untyped at the term level. A variable can stand for any term — an atom, a number, a list, a compound structure, or even another variable — and its “type” is determined by the shape of whatever it eventually unifies with. You never declare types; you inspect them with built-in predicates when you need to.
In this tutorial you’ll learn the five term categories that make up every Prolog program — atoms, numbers, variables, compound terms, and lists — and you’ll see why X = 3 in Prolog does not mean “store 3 in X” but rather “prove that X and 3 can be made identical.”
The Five Kinds of Terms
Every value in Prolog is a term, and every term belongs to exactly one of these categories:
| Term Kind | Example | Starts With |
|---|---|---|
| Atom | hello, 'Alice Smith', [] | lowercase letter or quoted |
| Number | 42, -17, 3.14 | digit or minus sign |
| Variable | X, Result, _Tmp, _ | uppercase letter or underscore |
| Compound term | foo(bar, 3), point(1, 2) | functor + arguments |
| List | [1, 2, 3], `[H | T]` |
The capitalization rule is load-bearing: alice is an atom (a constant), while Alice is a variable (a placeholder). Getting this wrong is the most common mistake new Prolog programmers make.
Unification Is Not Assignment
In imperative languages, x = 3 overwrites whatever was in x. In Prolog, X = 3 asks the engine: “Can X and 3 be made identical? If so, bind X to make it so.” This operation is called unification.
A variable can be bound only once within a proof. Once X is bound to 3, trying to unify it with 4 fails:
| |
This single-assignment property comes directly from Prolog’s mathematical foundation — a logical variable represents one specific (if unknown) value within a proof, not a memory cell that changes over time.
Unification also works structurally. Two compound terms unify if and only if their functors match and their arguments unify pairwise:
| |
A Complete Example
Create a file named variables.pl:
| |
What’s Happening
=performs unification. It never “evaluates” — it only matches shapes and binds variables.isis the only arithmetic operator.X is 10 + 5evaluates the right-hand side and unifiesXwith the result. WritingX = 10 + 5would bindXto the unevaluated term+(10, 5).point(X, Y) = point(3, 4)destructures in one step — there is no separate “pattern match” syntax because unification is pattern matching.[Head|Tail] = Numberssplits a list using the cons pattern[H|T]. The head is the first element, the tail is the rest.atom/1,integer/1,float/1,is_list/1, andcompound/1are type-inspection predicates. They succeed or fail rather than returning a type name.
Running with Docker
| |
The -q flag silences SWI-Prolog’s startup banner so only your program’s output appears.
Expected Output
Atom: hello
Quoted atom: Alice Smith
Integer: 30
Float: 3.14159
Sum: 15
Product: 42
Point: X = 3, Y = 4
Person: person(Bob,42,engineer)
List: [1,2,3,4,5]
Head: 1 Tail: [2,3,4,5]
Type checks:
hello -> atom
42 -> integer
3.14 -> float
[1,2,3] -> list
foo(bar,baz) -> compound
Atoms vs. Strings
Prolog has three text-like representations, and the distinction matters:
- Unquoted atom —
hello,foo_bar. Starts with lowercase. Fast, interned symbol. - Quoted atom —
'Alice Smith','hello world'. Needed when the text contains spaces, punctuation, or starts with uppercase. - String —
"hello". In SWI-Prolog 7+ these are a dedicated string type; in classical ISO Prolog they are lists of character codes. Stick with atoms unless you specifically need string operations.
| |
Unbound Variables and the Anonymous _
A variable that has not yet been bound is called unbound or fresh. You can test for this with var/1:
| |
The underscore _ is the anonymous variable — a fresh, unnamed variable. Every occurrence of _ is a different variable, which makes it useful for ignoring parts of a term:
| |
Names starting with underscore (_Temp, _Ignored) are also treated as “don’t-care” variables — the compiler won’t warn about them being singleton.
Key Concepts
- Everything is a term. Atoms, numbers, variables, and compound terms are the only things Prolog manipulates — there is no class/primitive divide.
- Case is semantic, not stylistic. Lowercase names are atoms; uppercase (or underscore-prefixed) names are variables.
- Unification replaces assignment.
=makes two terms identical by binding variables; it never mutates. - Single-assignment within a proof. Once a variable is bound, it stays bound until Prolog backtracks past that binding.
isevaluates arithmetic;=does not.X is 2 + 3bindsXto5;X = 2 + 3bindsXto the term+(2, 3).- Types are inspected, not declared. Predicates like
atom/1,integer/1,compound/1, andis_list/1ask “what shape does this term have?” at runtime. - Lists are compound terms in disguise.
[1, 2, 3]is sugar for'.'(1, '.'(2, '.'(3, []))), which is whycompound([1,2,3])succeeds. - The anonymous
_ignores values. Use it whenever you need to unify a position without caring what ends up there.
Comments
Loading comments...
Leave a Comment