Reference Manual
Reference Manual
Reference Manual
Fergus Henderson
Thomas Conway
Zoltan Somogyi
David Jeffery
Peter Schachte
Simon Taylor
Chris Speirs
Tyson Dowd
Ralph Becket
Mark Brown
Copyright
c 1995–2010 The University of Melbourne.
Permission is granted to make and distribute verbatim copies of this manual provided the
copyright notice and this permission notice are preserved on all copies.
Permission is granted to copy and distribute modified versions of this manual under the
conditions for verbatim copying, provided also that the entire resulting derived work is
distributed under the terms of a permission notice identical to this one.
Permission is granted to copy and distribute translations of this manual into another lan-
guage, under the above conditions for modified versions.
i
Table of Contents
1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2.1 Syntax overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2.2 Tokens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2.3 Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.4 Builtin Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.5 Items . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.6 Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.7 Facts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.8 Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.9 Goals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.10 State variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.11 DCG-rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.12 DCG-goals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.13 Data-terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.13.1 Data-functors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.13.2 Record syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.13.3 Unification expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.13.4 Conditional expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.13.5 Lambda expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.13.6 Higher-order function applications . . . . . . . . . . . . . . . . . . . . . . . 21
2.13.7 Explicit type qualification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.14 Variable scoping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.15 Implicit quantification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.16 Elimination of double negation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.1 Builtin types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.2 User-defined types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.2.1 Discriminated unions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.2.2 Equivalence types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.2.3 Abstract types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.3 Predicate and function type declarations . . . . . . . . . . . . . . . . . . . . . . 26
3.4 Field access functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.4.1 Field selection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.4.2 Field update . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.4.3 User-supplied field access function declarations. . . . . . . . . . . . 29
3.4.4 Field access examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.5 Solver types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.5.1 The ‘any’ inst . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.5.2 Abstract solver type declarations . . . . . . . . . . . . . . . . . . . . . . . . . 31
ii
4 Modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.1 Insts, modes, and mode definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.2 Predicate and function mode declarations . . . . . . . . . . . . . . . . . . . . . 36
4.3 Constrained polymorphic modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
4.4 Different clauses for different modes . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5 Unique modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.1 Destructive update . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.2 Backtrackable destructive update . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.3 Limitations of the current implementation . . . . . . . . . . . . . . . . . . . . . 42
6 Determinism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
6.1 Determinism categories. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
6.2 Determinism checking and inference . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
6.3 Replacing compile-time checking with run-time checking . . . . . . . 47
6.4 Interfacing nondeterministic code with the real world . . . . . . . . . . 48
6.5 Committed choice nondeterminism . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
8 Higher-order programming . . . . . . . . . . . . . . . . . . . 53
8.1 Creating higher-order terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
8.2 Calling higher-order terms. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
8.3 Higher-order modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
9 Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
9.1 The module system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
9.2 An example module. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
9.3 Sub-modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
9.3.1 Nested sub-modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
9.3.2 Separate sub-modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
9.3.3 Visibility rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
9.3.4 Implementation bugs and limitations . . . . . . . . . . . . . . . . . . . . . 62
9.4 Module initialisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
9.5 Module finalisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
9.6 Module-local mutable variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
iii
10 Type classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
10.1 Typeclass declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
10.2 Instance declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
10.3 Abstract typeclass declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
10.4 Abstract instance declarations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
10.5 Type class constraints on predicates and functions . . . . . . . . . . . . 70
10.6 Type class constraints on type class declarations . . . . . . . . . . . . . . 71
10.7 Type class constraints on instance declarations . . . . . . . . . . . . . . . 71
10.8 Functional dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
11 Existential types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
11.1 Existentially typed predicates and functions . . . . . . . . . . . . . . . . . . 74
11.1.1 Syntax for explicit type quantifiers . . . . . . . . . . . . . . . . . . . . . . 74
11.1.2 Semantics of type quantifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
11.1.3 Examples of correct code using type quantifiers . . . . . . . . . . 76
11.1.4 Examples of incorrect code using type quantifiers . . . . . . . . 76
11.2 Existential class constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
11.3 Existentially typed data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
11.4 Some idioms using existentially quantified types . . . . . . . . . . . . . . 79
12 Exception handling . . . . . . . . . . . . . . . . . . . . . . . . . . 81
13 Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
15 C interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
15.1 Calling C code from Mercury . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
15.1.1 pragma import . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
15.1.2 pragma c code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
15.1.3 Nondet pragma c code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
15.1.4 C code attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
15.1.5 Purity and side effects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
15.2 Including C headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
15.3 Including C code. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
15.4 Calling Mercury code from C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
15.5 Linking with C object files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
15.6 Passing data to and from C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
15.7 Using C pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
v
18 Pragmas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
18.1 Inlining . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
18.2 Type specialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
18.2.1 Syntax and semantics of type specialization pragmas . . . 128
18.2.2 When to use type specialization . . . . . . . . . . . . . . . . . . . . . . . . 128
18.2.3 Implementation specific details . . . . . . . . . . . . . . . . . . . . . . . . . 128
18.3 Obsolescence. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
18.4 Source file name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
20 Bibliography . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
[1] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
[2] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
[3] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
[4] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
[5] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
Chapter 2: Syntax 1
1 Introduction
Mercury is a new general-purpose programming language, designed and implemented by
a small group of researchers at the University of Melbourne, Australia. Mercury is based
on the paradigm of purely declarative programming, and was designed to be useful for the
development of large and robust “real-world” applications. It improves on existing logic
programming languages by providing increased productivity, reliability and efficiency, and
by avoiding the need for non-logical program constructs. Mercury provides the traditional
logic programming syntax, but also allows the syntactic convenience of user-defined func-
tions, smoothly integrating logic and functional programming into a single paradigm.
Mercury requires programmers to supply type, mode and determinism declarations for
the predicates and functions they write. The compiler checks these declarations, and rejects
the program if it cannot prove that every predicate or function satisfies its declarations. This
improves reliability, since many kinds of errors simply cannot happen in successfully com-
piled Mercury programs. It also improves productivity, since the compiler pinpoints many
errors that would otherwise require manual debugging to locate. The fact that declarations
are checked by the compiler makes them much more useful than comments to anyone who
has to maintain the program. The compiler also exploits the guaranteed correctness of the
declarations for significantly improving the efficiency of the code it generates.
To facilitate programming-in-the-large, to allow separate compilation, and to support
encapsulation, Mercury has a simple module system. Mercury’s standard library has a
variety of pre-defined modules for common programming tasks — see the Mercury Library
Reference Manual.
2 Syntax
2.2 Tokens
Tokens in Mercury are the same as in ISO Prolog. The only differences are the ‘#line ’
token, which is used as a line number directive (see below) and the backquote (‘‘’) token.
The different tokens are as follows. Tokens may be separated by whitespace or line
number directives.
Chapter 2: Syntax 2
2.3 Terms
Syntactically, terms in Mercury are exactly the same as in ISO Prolog, except that as
extensions we permit higher-order terms and the introduction of infix operators by the use
of grave accents (backquotes), as described below, and we support an extended set of builtin
operators. See Section 2.4 [Builtin Operators], page 4. Also, the constructor for list terms
in Mercury is [|]/2, not ./2 as in Prolog.
Note, however, that the meaning of some terms in Mercury is different to that in Prolog.
See Section 2.13 [Data-terms], page 17.
A term is either a variable or a functor.
A functor is an integer, a float, a string, a name, a compound term, or a higher-order
term.
A compound term is a simple compound term, a list term, a tuple term, an operator
term, or a parenthesized term.
Chapter 2: Syntax 4
The “Specifier” field indicates what structure terms constructed with an operator are
allowed to take. “f” represents the operator and “x” and “y” represent arguments. “x”
represents an argument whose priority must be strictly lower than that of the operator. “y”
represents an argument whose priority is lower or equal to that of the operator. For example,
“yfx” indicates a left-associative infix operator, while “xfy” indicates a right-associative infix
operator.
. yfx 10
! fx 40
!. fx 40
!: fx 40
@ xfx 90
^ xfy 99
^ fx 100
: yfx 120
1
‘op ‘ yfx 120
** xfy 200
- fx 200
\\ fx 200
* yfx 400
/ yfx 400
// yfx 400
<< yfx 400
>> yfx 400
div yfx 400
mod xfx 400
rem xfx 400
+ fx 500
+ yfx 500
++ xfy 500
- yfx 500
-- yfx 500
/\\ yfx 500
\\/ yfx 500
.. xfx 550
:= xfx 650
=^ xfx 650
< xfx 700
= xfx 700
=.. xfx 700
=:= xfx 700
=< xfx 700
== xfx 700
1
Operator term (see Section 2.3 [Terms], page 3).
Chapter 2: Syntax 6
solver fy 1181
end_module fx 1199
import_module fx 1199
include_module fx 1199
initialise fx 1199
initialize fx 1199
finalise fx 1199
finalize fx 1199
inst fx 1199
instance fx 1199
mode fx 1199
module fx 1199
pragma fx 1199
promise fx 1199
rule fx 1199
typeclass fx 1199
use_module fx 1199
--> xfx 1200
:- fx 1200
:- xfx 1200
?- fx 1200
2.5 Items
Each item in a Mercury module is either a declaration or a clause. If the top-level functor
of the term is ‘:-/1’, the item is a declaration, otherwise it is a clause. There are three
types of clauses. If the top-level functor of the item is ‘:-/2’, the item is a rule. If the
top-level functor is ‘-->/2’, the item is a DCG rule. Otherwise, the item is a fact. There
are two types of rules and facts. If the top-level functor of the head of a rule is ‘=/2’, the
rule is a function rule, otherwise it is a predicate rule. If the top-level functor of the head
of a fact is ‘=/2’, the fact is a function fact, otherwise it is a predicate fact.
2.6 Declarations
The allowed declarations are:
:- type
:- solver type
:- pred
:- func
:- inst
:- mode
:- typeclass
:- instance
:- pragma
:- promise
:- initialise
:- finalise
Chapter 2: Syntax 8
:- mutable
:- module
:- interface
:- implementation
:- import_module
:- use_module
:- include_module
:- end_module
The ‘type’, ‘pred’ and ‘func’ declarations are used for the type system, the ‘inst’ and
‘mode’ declarations are for the mode system, the ‘pragma’ declarations are for the C interface,
and for compiler hints about inlining, and the remainder are for the module system. They
are described in more detail in their respective chapters.
2.7 Facts
A function fact is an item of the form ‘Head = Result ’. A predicate fact is an item of the
form ‘Head ’, where the top-level functor of Head is not :-/1, :-/2, -->/2, or =/2. In both
cases, the Head term must not be a variable. The top-level functor of the Head determines
which predicate or function the fact belongs to; the predicate or function must have been
declared in a preceding ‘pred’ or ‘func’ declaration in this module. The Result (if any) and
the arguments of the Head must be valid data-terms (optionally annotated with a mode
qualifier; see Section 4.4 [Different clauses for different modes], page 39).
A fact is equivalent to a rule whose body is ‘true’.
2.8 Rules
A function rule is an item of the form ‘Head = Result :- Body ’. A predicate rule is an
item of the form ‘Head :- Body ’ where the top-level functor of ‘Head’ is not =/2. In both
cases, the Head term must not be a variable. The top-level functor of the Head determines
which predicate or function the clause belongs to; the predicate or function must have
been declared in a preceding ‘pred’ or ‘func’ declaration in this module. The Result and
the arguments of the Head must be valid data-terms (optionally annotated with a mode
qualifier; see Section 4.4 [Different clauses for different modes], page 39). The Body must
be a valid goal.
2.9 Goals
A goal is a term of one of the following forms:
some Vars Goal
An existential quantification. Vars must be a list of variables. Goal must be a
valid goal.
Each existential quantification introduces a new scope. The variables in Vars
are local to the goal Goal: for each variable named in Vars, any occurrences of
variables with that name in Goal are considered to name a different variable
than any variables with the same name that occur outside of the existential
quantification.
Chapter 2: Syntax 9
bind a non-local variable that is not listed in Vars (non-local variables with
inst any are assumed to be further constrained by Goal and must also be in-
cluded in Vars). If Goal has determinism ‘multi’ or ‘cc_multi’ then promise_
equivalent_solutions Vars Goal has determinism ‘det’. If Goal has deter-
minism ‘nondet’ or ‘cc_nondet’ then promise_equivalent_solutions Vars
Goal has determinism ‘semidet’.
only be referred to as ‘!.X ’ (unless the enclosing X is masked by a more local state variable
of the same name.)
For instance, the following goal employing an if-then-else expression
p((if q(!X ), r(!X ) then A else B ), !X )
is illegal because it implicitly refers to ‘!:X ’ in the condition of the if-then-else expression.
However
p((if some[!X ] (q(!X ), r(!X )) then A else B ), !X )
is acceptable because the state variable X is locally scoped to the condition and then-goal
of the if-then-else expression, hence ‘!:X ’ may appear therein.
There are three restrictions concerning state variables in lambdas: first, ‘!X ’ is not a
legitimate function result, since it stands for two arguments, rather than one; second, ‘!X ’
may not appear as a parameter term in the head of a lambda since there is no syntax
for specifying the modes of the two implied parameters; third, ‘!X ’ may not appear as
an argument in a function application since this would not make sense given the usual
interpretation of state variables and functions.
Head :- Body
transform((Head :- Body ), X, ThisX, NextX ) =
substitute(Head, X, ThisX, NextX ) :- transform(Body, X, ThisX, NextX )
Head --> Body
transform((Head --> Body ), X, ThisX, NextX ) =
substitute(Head, X, ThisX, NextX ) :- transform(Body, X, ThisX, NextX )
Goal1, Goal2
transform((Goal1, Goal2 ), X, ThisX, NextX ) =
transform(Goal1, X, ThisX, TmpX ), transform(Goal2, X, TmpX, NextX )
for some fresh variable TmpX.
Goal1 ; Goal2
transform((Goal1 ; Goal2 ), X, ThisX, NextX ) =
transform(Goal1, X, ThisX, NextX ) ; transform(Goal2, X, ThisX, NextX )
not Goal
\+ Goal A negation. The two different syntaxes have identical semantics.
transform((not Goal ), X, ThisX, NextX ) =
not transform(Goal1, X, ThisX, DummyX ), NextX = ThisX
for some fresh variable DummyX.
if Goal1 then Goal2 else Goal3
Goal1 -> Goal2 ; Goal3
An if-then-else. The two different syntaxes have identical semantics.
transform((if Goal1 then Goal2 else Goal3 ), X, ThisX, NextX ) =
if transform(Goal1, X, ThisX, TmpX ) then transform(Goal2, X, TmpX, NextX )
else transform(Goal3, X, ThisX, NextX )
for some fresh variable TmpX.
Chapter 2: Syntax 14
2.11 DCG-rules
(DCG notation is intended for writing parsers and sequence generators in a particular style;
in the past it has also been used to thread an implicit state variable, typically the IO
state, through code. As a matter of style, we recommend that in future DCG notation be
reserved for writing parsers and sequence generators and that state variable syntax be used
for passing state threads.)
DCG-rules in Mercury have identical syntax and semantics to DCG-rules in Prolog.
A DCG-rule is an item of the form ‘Head --> Body ’. The Head term must not be a vari-
able. A DCG-rule is an abbreviation for an ordinary rule with two additional implicit argu-
ments appended to the arguments of Head. These arguments are fresh variables which we
shall call V in and V out. The Body must be a valid DCG-goal, and is an abbreviation for
an ordinary goal. The next section defines a mathematical function ‘DCG-transform(V_in,
V_out, DCG-goal )’ which specifies the semantics of how DCG goals are transformed into
ordinary goals. (The ‘DCG-transform’ function is purely for the purposes of exposition, to
define the semantics — it is not part of the language.)
2.12 DCG-goals
A DCG-goal is a term of one of the following forms:
some Vars DCG-goal
A DCG existential quantification. Vars must be a list of variables. DCG-goal
must be a valid DCG-goal.
Semantics:
transform(V_in, V_out, some Vars DCG_goal) =
some Vars transform(V_in, V_out, DCG_goal)
all Vars DCG-goal
A DCG universal quantification. Vars must be a list of variables. DCG-goal
must be a valid DCG-goal.
Semantics:
transform(V_in, V_out, all Vars DCG_goal) =
all Vars transform(V_in, V_out, DCG_goal)
DCG-goal1, DCG-goal2
A DCG sequence. Intuitively, this means “parse DCG-goal1 and then parse
DCG-goal2” or “do DCG-goal1 and then do DCG-goal2”. (Note that the only
way this construct actually forces the desired sequencing is by the modes of the
Chapter 2: Syntax 16
2.13 Data-terms
Syntactically, a data-term is just a term.
There are a couple of differences from Prolog. The first one is that double-quoted
strings are atomic in Mercury, they are not abbreviations for lists of character codes. The
second is that Mercury provides several extensions to Prolog’s term syntax: Mercury terms
may contain record field selection and field update expressions, conditional (if-then-else)
Chapter 2: Syntax 18
2.13.1 Data-functors
A data-functor is an integer, a float, a string, a character literal (any single-character
name), a name, an implementation-defined literal, or a compound data-term. A compound
data-term is a compound term which does not match the form of a special data-term (see
Section 2.13 [Data-terms], page 17), and whose arguments are data-terms. If a data-functor
is a name or a compound data-term, its top-level functor must name a function, predicate,
or data constructor declared in the program or in the interface of an imported module.
Implementation-defined literals are symbolic names whose value represents a property
of the compilation environment or the context in which it appears. The implementation
replaces these symbolic names with actual constants during compilation. Implementation-
defined literals can only appear within clauses. The following literals must be supported by
all Mercury implementations:
‘$file’ a string that gives the name of the file that contains the module being compiled.
If the name of the file cannot be determined then it is replaced by an arbitrary
string.
‘$line’ the line number (integer) of the goal in which the literal appears or -1 if it
cannot be determined.
‘$module’ a string representation of the fully-qualified module name.
‘$pred’ a string containing the fully-qualified predicate or function name and arity.
The Mercury Melbourne implementation additionally supports the following extension:
‘$grade’ the grade (string) in which the module is compiled.
Term ^ field_list
A field selection. For each field specifier in field list, apply the corresponding
selection function in turn.
Term must be a valid data-term. field list must be a valid field list.
A field selection is transformed using the following rules:
transform(Term ^ Field(Arg1, ...)) = Field(Arg1, ..., Term).
transform(Term ^ Field(Arg1, ...) ^ Rest) =
transform(Field(Arg1, ..., Term) ^ Rest).
Examples:
Term ^ field is equivalent to field(Term).
Term ^ field(Arg) is equivalent to field(Arg, Term).
Term ^ field1(Arg1) ^ field2(Arg2, Arg3) is equivalent to
field2(Arg2, Arg3, field1(Arg1, Term)).
Term ^ field_list := FieldValue
A field update, returning a copy of Term with the value of the field specified
by field list replaced with FieldValue.
Term must be a valid data-term. field list must be a valid field list.
A field update is transformed using the following rules:
transform(Term ^ Field(Arg1, ...) := FieldValue) =
’Field :=’(Arg1, ..., Term, FieldValue)).
For example
p(X @ f(_, _), X).
is equivalent to
p(H1, H2) :-
H1 = X,
H1 = f(_, _),
H2 = X.
Unification expressions are most useful when writing switches (see Section 6.2 [Deter-
minism checking and inference], page 44). The arguments of a unification expression are
examined when checking for switches. The arguments of an equivalent user-defined function
would not be.
The three different variable sorts occupy different namespaces: there is no semantic re-
lationship between two variables of different sorts (e.g. a type variable and an ordinary
variable) even if they happen to share the same name. (However, as a matter of program-
ming style, it is generally a bad idea to use the same name for variables of different sorts
in the same clause.)
The scope of ordinary variables is the clause or declaration in which they occur, unless
they are quantified, either explicitly (see Section 2.9 [Goals], page 8) or implicitly (see
Section 2.15 [Implicit quantification], page 22).
The scope of type variables in a predicate or function’s type declaration extends over
any explicit type qualifications (see Section 2.13.7 [Explicit type qualification], page 21) in
the clauses for that predicate or function, and over ‘pragma type_spec’ (see Section 18.2
[Type specialization], page 128) declarations for that predicate or function, so that explicit
type qualifications and ‘pragma type_spec’ declarations can refer to those type variables.
The scope of any type variables in an explicit type qualification which do not occur in the
predicate or function’s type declaration is the clause in which they occur.
The scope of inst variables is the clause or declaration in which they occur.
3 Types
The type system is based on many-sorted logic, and supports polymorphism, type classes
(see Chapter 10 [Type classes], page 65), and existentially quantified types (see Chapter 11
[Existential types], page 74).
of the functor must be distinct type variables. The body term is defined as a sequence of
constructor definitions separated by semi-colons.
Ordinarily, each constructor definition must be a functor whose arguments (if any) are
types. Ordinary discriminated union definitions must be transparent: all type variables
occurring in the body must also occur in the type.
However, constructor definitions can optionally be existentially typed. In that case, the
functor will be preceded by an existential type quantifier and can optionally be followed by
an existential type class constraint. For details, see Chapter 11 [Existential types], page 74.
Existentially typed discriminated union definitions need not be transparent.
The arguments of constructor definitions may be labelled. These labels cause the com-
piler to generate functions which can be used to conveniently select and update fields of a
term in a manner independent of the definition of the type (see Section 3.4 [Field access
functions], page 28). A labelled argument has the form fieldname :: Type . It is an error
for two fields in the same module to have the same label.
Here are some examples of discriminated union definitions:
:- type fruit
---> apple
; orange
; banana
; pear.
:- type strange
---> foo(int)
; bar(string).
:- type employee
---> employee(
name :: string,
age :: int,
department :: string
).
:- type tree
---> empty
; leaf(int)
; branch(tree, tree).
:- type list(T)
---> []
; [T | list(T)].
difficult to define a type whose constructors include ’;’/2. To allow this, curly braces
can be used to quote the semi-colon. It is then also necessary to quote curly braces. The
following example illustrates this:
:- type tricky
---> { int ; int }
; { { int } }.
This defines a type with two constructors, ’;’/2 and ’{}’/1, whose argument types are
all int. We recommend against using constructors named ’{}’ because of the possibility
of confusion with the builtin tuple types.
Each discriminated union type definition introduces a distinct type. Mercury considers
two discriminated union types that have the same bodies to be distinct types (name equiv-
alence). Having two different definitions of a type with the same name and arity in the
same module is an error.
Constructors may be overloaded among different types: there may be any number of
constructors with a given name and arity, so long as they all have different types. However,
there must not be more than one constructor with the same name, arity, and result type in
the same module. (There is no particularly good reason for this restriction; in the future
we may allow several such functors as long as they have different argument types.) Note
that excessive overloading of constructors can slow down type checking and can make the
program confusing for human readers, so overloading should not be over-used.
Note that a predicate defined using DCG notation (see Section 2.11 [DCG-rules], page 15)
will appear to be defined with two fewer arguments than it is declared with. It will also
appear to be called with two fewer arguments when called from predicates defined using
DCG notation. However, when called from an ordinary predicate or function, it must have
all the arguments it was declared with.
The compiler infers the types of data-terms, and in particular the types of variables and
overloaded constructors, functions, and predicates. A type assignment is an assignment of a
type to every variable and of a particular constructor, function, or predicate to every name
in a clause. A type assignment is valid if it satisfies the following conditions.
Each constructor in a clause must have been declared in at least one visible type declara-
tion. The type assigned to each constructor term must match one of the type declarations
for that constructor, and the types assigned to the arguments of that constructor must
match the argument types specified in that type declaration.
The type assigned to each function call term must match the return type of one of the
‘:- func’ declarations for that function, and the types assigned to the arguments of that
function must match the argument types specified in that type declaration.
The type assigned to each predicate argument must match the type specified in one of
the ‘:- pred’ declarations for that predicate. The type assigned to each head argument in
a predicate clause must exactly match the argument type specified in the corresponding ‘:-
pred’ declaration.
The type assigned to each head argument in a function clause must exactly match the
argument type specified in the corresponding ‘:- func’ declaration, and the type assigned
to the result term in a function clause must exactly match the result type specified in the
corresponding ‘:- func’ declaration.
The type assigned to each data-term with an explicit type qualification (see Section 2.13.7
[Explicit type qualification], page 21) must match the type specified by the type qualification
expression2 .
(Here “match” means to be an instance of, i.e. to be identical to for some substitution
of the type parameters, and “exactly match” means to be identical up to renaming of type
parameters.)
One type assignment A is said to be more general than another type assignment B if
there is a binding of the type parameters in A that makes it identical (up to renaming
of parameters) to B. If there is more than one valid type assignment, the compiler must
choose the most general one. If there are two valid type assignments which are not identical
up to renaming and neither of which is more general than the other, then there is a type
ambiguity, and compiler must report an error. A clause is type-correct if there is a unique
(up to renaming) most general valid type assignment. Every clause in a Mercury program
must be type-correct.
2
The type of an explicitly type qualified term may be an instance of the type specified by the qualifier.
This allows explicit type qualifications to constrain the types of two data-terms to be identical, without
knowing the exact types of the data-terms. It also allows type qualifications to refer to the types of the
results of existentially typed predicates or functions.
Chapter 3: Types 28
:- type unsettable
---> some [T] unsettable(
unsettable1 :: T,
unsettable2 :: T
).
:- type type2
---> type2(
field3 :: int,
Chapter 3: Types 30
field4 :: int
).
The compiler generates some field access functions for ‘field1’. The functions generated
for the other fields are similar.
+:- func type1 ^ field1 = type2.
+type1(Field1, _) ^ field1 = Field1.
+Term0 ^ increment_field3 =
Term0 ^ field1 ^ field3 := Term0 ^ field1 ^ field3 + 1.
The compiler expands this into
incremental_field3(Term0) = Term :-
OldField3 = field3(field1(Term0)),
OldField1 = field1(Term0),
NewField1 = ’field3 :=’(OldField1, OldField3 + 1),
Term = ’field1 :=’(Term0, NewField1).
The field access functions defined in the Mercury standard library module ‘map’ can be
used as follows:
:- func update_field_in_map(map(int, type1), int, string)
= map(int, type1) is semidet.
form an equivalence class; if a variable is non-ground then the set of values it unifies with
do not form an equivalence class.
More formally, X is ground if for values Y and Z that unify with X, it is the case that Y
and Z also unify with each other. X is non-ground if there are values Y and Z that unify
with X, but which do not unify with each other.
A non-solver type value will have inst any if it is constructed using one or more inst any
values.
The built-in modes ia and oa are equivalent to in(any) and out(any) respectively.
in a negated context then, as in the case of solver variables, a promise will be required
around the negation or if-then-else.
Pred and func expressions with inst any are written using any_pred and any_func in
place of pred and func, respectively.
The third possibility is that the higher order value can be given an impure type (see
Section 16.8 [Higher-order impurity], page 123).
4 Modes
type values; see Section 3.5 [Solver types], page 30 for more detail). The shape of these
trees is determined by the type of the variable to which they apply.
A more concise, alternative syntax exists for bound instantiatedness trees:
:- inst maybeskel ---> no ; yes(ground).
which is equivalent to writing
:- inst maybeskel == bound(no ; yes(ground)).
As execution proceeds, variables may become more instantiated. A mode mapping is
a mapping from an initial instantiatedness tree to a final instantiatedness tree, with the
constraint that no node of the type tree is transformed from bound to free. Mercury allows
the user to specify mode mappings directly by expressions such as inst1 >> inst2, or to
give them a name using declarations such as
:- mode m == inst1 >> inst2.
Two standard shorthand modes are provided, corresponding to the standard notions of
inputs and outputs:
:- mode in == ground >> ground.
:- mode out == free >> ground.
Prolog fans who want to use the symbols ‘+’ and ‘-’ can do so by simply defining them
using a mode declaration:
:- mode (+) == in.
:- mode (-) == out.
These two modes are enough for most functions and predicates. Nevertheless, Mercury’s
mode system is sufficiently expressive to handle more complex data-flow patterns, including
those involving partially instantiated data structures. (The current implementation does
not handle partially instantiated data structures yet.)
For example, consider an interface to a database that associates data with keys, and
provides read and write access to the items it stores. To represent accesses to the database
over a network, you would need declarations such as
:- type operation
---> lookup(key, data)
; set(key, data).
:- inst request
---> lookup(ground, free)
; set(ground, ground).
:- mode create_request == free >> request.
:- mode satisfy_request == request >> ground.
‘inst’ and ‘mode’ declarations can be parametric. For example, the following declaration
:- inst maybeskel(Inst) ---> no ; yes(Inst).
defines the inst ‘listskel(Inst)’ to be a list skeleton whose elements have inst ‘Inst’; you
can the use insts such as ‘listskel(listskel(free))’, which represents the instantiation
state of a list of lists of free variables. The standard library provides the parametric modes
:- mode in(Inst) == Inst >> Inst.
:- mode out(Inst) == free >> Inst.
so that for example the mode ‘create_request’ defined above could have be defined as
Chapter 4: Modes 36
Also, if the constraint on an inst parameter is ‘ground’ then it is not necessary to give
the constraint in the declaration. The example can be further shortened to
:- mode append(in(list_skel(I)), in(list_skel(I)), out(list_skel(I)))
is det.
Constrained polymorphic modes are particularly useful when passing objects with higher-
order types to polymorphic predicates since they allow the higher-order mode information
to be retained (see Chapter 8 [Higher-order], page 53).
5 Unique modes
Mode declarations can also specify so-called “unique modes”. Mercury’s unique modes are
similar to “linear types” in some functional programming languages such as Clean. They
allow you to specify when there is only one reference to a particular value, and when there
will be no more references to that value. If the compiler knows there will be no more
references to a value, it can perform “compile-time garbage collection” by automatically
inserting code to deallocate the storage associated with that value. Even more importantly,
Chapter 5: Unique modes 41
the compiler can also simply reuse the storage immediately, for example by destructively
updating one element of an array rather than making a new copy of the entire array in
order to change one element. Unique modes are also the mechanism Mercury uses to
provide declarative I/O.
We have not yet implemented unique modes fully, and the details are still in a state of
flux. So the following should be considered tentative.
% unique input
:- mode ui == unique >> unique.
% destructive input
:- mode di == unique >> dead.
Mode ‘uo’ is used to create a unique value. Mode ‘ui’ is used to inspect a unique value
without losing its uniqueness. Mode ‘di’ is used to deallocate or reuse the memory occupied
by a value that will not be used.
Note that a value is not considered ‘unique’ if it might be needed on backtracking. This
means that unique modes are generally only useful for code whose determinism is ‘det’ or
‘cc_multi’ (see Chapter 6 [Determinism], page 42).
Unlike ‘bound’ instantiatedness trees, there is no alternative syntax for ‘unique’ instan-
tiatedness trees.
‘mostly_unique’ and ‘mostly_dead’ are equivalent to ‘unique’ and ‘dead’, except that only
references which will be encountered during forward execution are counted — it is OK for
‘mostly_unique’ or ‘mostly_dead’ values to be needed again on backtracking.
Mercury defines some standard modes for manipulating “mostly unique” values, just as
it does for unique values:
% mostly unique output
:- mode muo == free >> mostly_unique.
6 Determinism
:- pred p is det.
p.
Chapter 6: Determinism 44
:- pred q is failure.
q :- fail.
If there is no mode declaration for a function, then the default mode for that function
is considered to have been declared as ‘det’. If you want to write a partial function,
i.e. one whose determinism is ‘semidet’, then you must explicitly declare the mode and
determinism.
In Mercury, a function is supposed to be a true mathematical function of its arguments;
that is, the value of the function’s result should be determined only by the values of its
arguments. Hence, for any mode of a function that specifies that all the arguments are fully
input (i.e. for which the initial inst of all the arguments is a ground inst), the determinism
of that mode can only be ‘det’, ‘semidet’, ‘erroneous’, or ‘failure’.
The determinism categories form this lattice:
erroneous
/ \
failure det
\ / \
semidet multi
\ /
nondet
The higher up this lattice a determinism category is, the more the compiler knows about
the number of solutions of procedures of that determinism.
be based on the goal succeeding at most once, since the compiler will implicitly prune away
any duplicate solutions.
Calls The determinism category of a call is the determinism declared or inferred for
the called mode of the called procedure.
Unifications
The determinism of a unification is either det, semidet, or failure, depending
on its mode.
A unification that assigns the value of one variable to another is deterministic.
A unification that constructs a structure and assigns it to a variable is also de-
terministic. A unification that tests whether a variable has a given top function
symbol is semideterministic, unless the compiler knows the top function symbol
of that variable, in which case its determinism is either det or failure depending
on whether the two function symbols are the same or not. A unification that
tests two variables for equality is semideterministic, unless the compiler knows
that the two variables are aliases for one another, in which case the unification
is deterministic, or unless the compiler knows that the two variables have dif-
ferent function symbols in the same position, in which case the unification has
a determinism of failure.
The compiler knows the top function symbol of a variable if the previous part
of the procedure definition contains a unification of the variable with a function
symbol, or if the variable’s type has only one function symbol.
Conjunctions
The determinism of the empty conjunction (the goal ‘true’) is det. The con-
junction ‘(A, B )’ can fail if either A can fail, or if A can succeed at least once,
and B can fail. The conjunction can succeed at most zero times if either A or B
can succeed at most zero times. The conjunction can succeed more than once
if either A or B can succeed more than once and both A and B can succeed at
least once. (If e.g. A can succeed at most zero times, then even if B can suc-
ceed many times the maximum number of solutions of the conjunction is still
zero.) Otherwise, i.e. if both A and B succeed at most once, the conjunction
can succeed at most once.
Switches A disjunction is a switch if each disjunct has near its start a unification that
tests the same bound variable against a different function symbol. For example,
consider the common pattern
(
L = [], empty(Out)
;
L = [H|T], nonempty(H, T, Out)
)
If L is input to the disjunction, then the disjunction is a switch on L.
If two variables are unified with each other, then whatever function symbol one
variable is unified with, the other variable is considered to be unified with the
same function symbol. In the following example, since K is unified with L, the
second disjunct unifies L as well as K with cons, and thus the disjunction is
recognized as a switch.
Chapter 6: Determinism 46
(
L = [], empty(Out)
;
K = L, K = [H|T], nonempty(H, T, Out)
)
A switch can fail if the various arms of the switch do not cover all the function
symbols in the type of the switched-on variable, or if the code in some arms
of the switch can fail, bearing in mind that in each arm of the switch, the
unification that tests the switched-on variable against the function symbol of
that arm is considered to be deterministic. A switch can succeed several times
if some arms of the switch can succeed several times, possibly because there are
multiple disjuncts that test the switched-on variable against the same function
symbol. A switch can succeed at most zero times only if all arms of the switch
can succeed at most zero times.
Only unifications may occur before the test of the switched-on variable in each
disjunct. Tests of the switched-on variable may occur within existential quan-
tification goals.
The following example is a switch.
(
Out = 1, L = []
;
some [H, T] (
L = [H|T],
nonempty(H, T, Out)
)
)
The following example is not a switch because the call in the first disjunct
occurs before the test of the switched-on variable.
(
empty(Out), L = []
;
L = [H|T], nonempty(H, T, Out)
)
The unification of the switched-on variable with a function symbol may occur
inside a nested disjunction in a given disjunct, provided that unification is
preceded only by other unifications, both inside the nested disjunction and
before the nested disjunction. The following example is a switch on X, provided
X is bound beforehand.
(
X = f
p(Out)
;
Y = X,
(
Y = g,
Chapter 6: Determinism 47
Intermediate = 42
;
Z = Y,
Z = h(Arg),
q(Arg, Intermediate)
),
r(Intermediate, Out)
)
Disjunctions
The determinism of the empty disjunction (the goal ‘fail’) is failure. A
disjunction ‘(A ; B )’ that is not a switch can fail if both A and B can fail. It
can succeed at most zero times if both A and B can succeed at most zero times.
It can succeed at most once if one of A and B can succeed at most once and
the other can succeed at most zero times. Otherwise, i.e. if either A or B can
succeed more than once, or if both A and B can succeed at least once, it can
succeed more than once.
If-then-else
If the condition of an if-then-else cannot fail, the if-then-else is equivalent to
the conjunction of the condition and the “then” part, and its determinism is
computed accordingly. Otherwise, an if-then-else can fail if either the “then”
part or the “else” part can fail. It can succeed at most zero times if the “else”
part can succeed at most zero times and if at least one of the condition and the
“then” part can succeed at most zero times. It can succeed more than once if
any one of the condition, the “then” part and the “else” part can succeed more
than once.
Negations
If the determinism of the negated goal is erroneous, then the determinism of
the negation is erroneous. If the determinism of the negated goal is failure,
the determinism of the negation is det. If the determinism of the negated goal
is det or multi, the determinism of the negation is failure. Otherwise, the
determinism of the negation is semidet.
p(A, B) :-
(
something_complicated(A, B)
;
B = A
).
Chapter 6: Determinism 48
‘p/2’ can have more than one solution only if ‘something_complicated’ can succeed.)
Sometimes, the rules specified by the Mercury language for determinism inference will infer
a determinism that is not as precise as you would like. However, it is generally easy to
overcome such problems. The way to do this is to replace the compiler’s static checking
with some manual run-time checking. For example, if you know that a particular goal
should never fail, but the compiler infers that goal to be semidet, you can check at runtime
that the goal does succeed, and if it fails, call the library predicate ‘error/1’.
:- pred q(T, T).
:- mode q(in, out) is det.
q(A, B) :-
( goal_that_should_never_fail(A, B0) ->
B = B0
;
error("goal_that_should_never_fail failed!")
).
The predicate error/1 has determinism erroneous, which means the compiler knows that it
will never succeed or fail, so the inferred determinism for the body of q/2 is det. (Checking
assumptions like this is good coding style anyway. The small amount of up-front work
that Mercury requires is paid back in reduced debugging time.) Mercury’s mode analysis
knows that computations with determinism erroneous can never succeed, which is why it
does not require the “else” part to generate a value for ‘B’. The introduction of the new
variable ‘B0’ is necessary because the condition of an if-then-else is a negated context, and
can export the values it generates only to the “then” part of the if-then-else, not directly to
the surrounding computation. (If the surrounding computations had direct access to values
generated in conditions, they might access them even if the condition failed.)
The first way is the one mentioned above: if a goal has no non-local output variables
then the implementation will only attempt to satisfy the goal once. Any potential duplicate
solutions will be implicitly pruned away.
The second way is to rely on the fact that the implementation will only seek a single
solution to ‘main/2’, so alternative solutions to ‘main/2’ (and hence also to nondet or multi
predicates called directly or indirectly from ‘main/2’) are implicitly pruned away. This is
one way to achieve “don’t care” style nondeterminism in Mercury.
The other situation in which you may want pruning and committed choice style nonde-
terminism is when you know that all the solutions returned will be equivalent. For example,
you might want to find the maximum element in a set by iterating over the elements in
the set. Iterating over the elements in a set in an unspecified order is a nondeterministic
operation, but no matter which order you remove them, the maximum value in the set
should be the same.
If you know that there will only ever be at most one distinct solution under the equal-
ity theory of the output variables, then you can use a ‘promise_equivalent_solutions’
determinism cast.
Note that specifying a user-defined equivalence relation as the equality predicate for
user-defined types (see Chapter 7 [User-defined equality and comparison], page 50) means
that ‘promise_equivalent_solutions’ can be used to express more general forms of equiv-
alence. For example, if you define a set type which represents sets as unsorted lists, you
would want to define a user-defined equivalence relation for that type, which could sort
the lists before comparing them. The ‘promise_equivalent_solutions’ determinism cast
could then be used for sets even though the lists used to represent the sets might not be in
the same order in every solution.
The compiler will check that all calls to a committed-choice mode of a predicate (or
function) do indeed occur in a single-solution context.
You can declare two different modes of a predicate (or function) which differ only in
“cc-ness” (i.e. one being ‘multi’ and the other ‘cc_multi’, or one being ‘nondet’ and the
other ‘cc_nondet’). In that case, the compiler will select the appropriate one for each call
depending on whether the call comes from a single-solution context or not. Calls from
single-solution contexts will call the committed choice version, while calls which are not
from single-solution contexts will call the backtracking version.
There are several reasons to use committed choice determinism annotations. One reason
is for efficiency: committed choice annotations allow the compiler to generate much more
efficient code. Another reason is for doing I/O, which is allowed only in ‘det’ or ‘cc_multi’
predicates, not in ‘multi’ predicates. Another is for dealing with types that use non-
canonical representations (see Chapter 7 [User-defined equality and comparison], page 50).
And there are a variety of other applications.
:- implementation.
:- import_module list.
:- type set(T) ---> set(list(T)).
In this example, the concrete representations ‘set([1,2])’ and ‘set([2,1])’ would both
represent the same abstract value, namely the set containing the elements 1 and 2.
For types such as this, which do not have a canonical representation, the standard
definition of equality is not the desired one; we want equality on sets to mean equality of
the abstract values, not equality of their representations. To support such types, Mercury
allows programmers to specify a user-defined equality predicate for user-defined types:
:- type set(T) ---> set(list(T))
where equality is set_equals.
Here ‘set_equals’ is the name of a user-defined predicate that is used for equality on the
type ‘set(T)’. It could for example be defined in terms of a ‘subset’ predicate.
:- pred set_equals(set(T)::in, set(T)::in) is semidet.
set_equals(S1, S2) :-
subset(S1, S2),
subset(S2, S1).
A comparison predicate can also be supplied.
Chapter 7: User-defined equality and comparison 51
:- pred set_compare(builtin.comparison_result::uo,
set(T)::in, set(T)::in) is det.
set_compare(Result, Set1, Set2) :-
promise_equivalent_solutions [Result] (
set_compare_2(Set1, Set2, Result)
).
Types with user-defined equality can only be used in limited ways. Because there multi-
ple representations for the same abstract value, any attempt to examine the representation
of such a value is a conceptually non-deterministic operation. In Mercury this is modelled
using committed choice nondeterminism.
The semantics of specifying ‘where equality is equalitypred ’ on the type declaration
for a type T are as follows:
• If the program contains any deconstruction unification or switch on a variable of type T
that could fail, other than unifications with mode ‘(in, in)’, then it is a compile-time
error.
• If the program contains any deconstruction unification or switch on a variable of type
T that cannot fail, then that operation has determinism ‘cc_multi’.
• Any attempts to examine the representation of a variable of type T using facilities of
the standard library (e.g. ‘argument’/3 and ‘functor/3’ in ‘deconstruct’) that do not
have determinism ‘cc_multi’ or ‘cc_nondet’ will result in a run-time error.
• In addition to the usual equality axioms, the declarative semantics of the program will
contain the axiom ‘X = Y <=> equalitypred (X, Y)’ for all X and Y of type ‘T’.
• Any ‘(in, in)’ unifications for type T are computed using the specified predicate
equalitypred.
A type declaration for a type ‘foo(T1, ..., TN)’ may contain a ‘where comparison is
comparepred ’ specification only if it declares a discriminated union type or a foreign type
(see Section 14.4 [Using foreign types from Mercury], page 93) and the following conditions
are satisfied:
• comparepred must be the name of a predicate with signature
:- pred comparepred (builtin.comparison_result::uo,
foo(T1, ..., TN)::in, foo(T1, ..., TN)::in) is det.
As with equality predicates, it is legal for the type, mode and determinism to be more
permissive.
• If the type is a discriminated union then its definition cannot be a single zero-arity
constructor.
• The comparison predicate must also be “pure” (see Chapter 16 [Impurity], page 119).
• The comparison predicate must be defined in the same module as the type.
• If the type is exported the comparison predicate must also be exported.
• The relation
compare_eq(X, Y) :- comparepred ((=), X, Y).
must be an equivalence relation; that is, it must be symmetric, reflexive, and transitive.
The compiler is not required to check this.
• The relations
compare_leq(X, Y) :- comparepred (R, X, Y), (R = (=) ; R = (<)).
compare_geq(X, Y) :- comparepred (R, X, Y), (R = (=) ; R = (>)).
must be total order relations: that is they must be antisymmetric, reflexive and tran-
sitive. The compiler is not required to check this.
Chapter 8: Higher-order programming 53
For each type for which the declaration has a ‘where comparison is comparepred ’ spec-
ification, any calls to the standard library predicate ‘builtin.compare/3’ with arguments
of that type are evaluated as if they were calls to comparepred.
A type declaration may contain a ‘where equality is equalitypred, comparison is
comparepred ’ specification only if in addition to the conditions above, ‘all [X, Y] (com-
parepred ((=), X, Y) <=> equalitypred (X, Y))’. The compiler is not required to check
this.
8 Higher-order programming
Mercury supports higher-order functions and predicates with currying, closures, and lambda
expressions. (To be pedantic, it would be more accurate to say that Mercury supports
higher-order procedures: in Mercury, when you construct a higher-order term, you only get
one mode of a predicate or function; if you want multiple modes, you must pass multiple
higher-order procedures.)
You can also create higher-order function terms of non-zero arity and higher-order pred-
icate terms by “currying”, i.e. specifying the first few arguments to a predicate or function,
but leaving the remaining arguments unspecified. For example, the unification
Sum123 = sum([1,2,3])
binds ‘Sum123’ to a higher-order predicate term of type ‘pred(int)’. Similarly, the unifi-
cation
Double = scalar_product(2)
binds ‘Double’ to a higher-order function term of type ‘func(list(int)) = list(int)’.
As a special case, currying of a multi-moded predicate or function is allowed provided
that the mode of the predicate or function can be determined from the insts of the higher-
order curried arguments. For example, ‘P = list.foldl(io.write)’ is allowed because the
inst of ‘io.write’ matches exactly one mode of ‘list.foldl’.
For higher-order predicate expressions that thread an accumulator pair, we have syntax
that allows you to use DCG notation in the goal of the expression. For example,
Pred = (pred(Strings::in, Num::out, di, uo) is det -->
io.write_string("The strings are: "),
{ list.length(Strings, Num) },
io.write_strings(Strings),
io.nl
)
is equivalent to
Pred = (pred(Strings::in, Num::out, IO0::di, IO::uo) is det :-
io.write_string("The strings are: ", IO0, IO1),
list.length(Strings, Num),
io.write_strings(Strings, IO1, IO2),
io.nl(IO2, IO)
)
Higher-order function terms of zero arity can only be created using an explicit lambda
expression; you have to use e.g. ‘(func) = foo’ rather than plain ‘foo’, because the latter
denotes the result of evaluating the function, rather than the function itself.
Note that when constructing a higher-order term, you cannot just use the name of a
builtin language construct such as ‘=’, ‘\=’, ‘call’, or ‘apply’, and nor can such constructs
be curried. Instead, you must either use an explicit lambda expression, or you must write
a forwarding predicate or function. For example, instead of
list.filter(\=(2), [1, 2, 3], List)
you must write either
list.filter((pred(X::in) is semidet :- X \= 2), [1, 2, 3], List)
or
list.filter(not_equal(2), [1, 2, 3], List)
where you have defined ‘not_equal’ using
:- pred not_equal(T::in, T::in) is semidet.
not_equal(X, Y) :- X \= Y.
Chapter 8: Higher-order programming 55
Another case when this arises is when want to curry a higher-order term. Suppose,
for example, that you have a higher-order predicate term ‘OldPred’ of type ‘pred(int,
char, float)’, and you want to construct a new higher-order predicate term ‘NewPred’ of
type ‘pred(char, float)’ from ‘OldPred’ by supplying a value for just the first argument.
The solution is the same: use an explicit lambda expression or a forwarding predicate. In
either case, the body of the lambda expression or the forwarding predicate must contain a
higher-order call with all the arguments supplied.
If you want to define a predicate which returns a higher-order predicate term, you would
use a mode such as ‘free >> pred(...) is ...’, or ‘out(pred(...) is ... )’. For exam-
ple:
:- pred foo(pred(int)).
:- mode foo(free >> pred(out) is det) is det.
foo(sum([1,2,3])).
Note that in Mercury it is an error to attempt to unify two higher-order terms. This is
because equivalence of higher-order terms is undecidable in the general case.
is illegal. If you really want to compare higher-order predicates for equivalence, you must
program it yourself; for example, the above goal could legally be written as
Note that the compiler will only catch direct attempts at higher-order unifications; in-
direct attempts (via polymorphic predicates, for example ‘(list.append([], [P], [Q])’
may result in an error at run-time rather than at compile-time.
In order to call a higher-order term, the compiler must know its higher-order inst. This
can cause problems when higher-order terms are placed into a polymorphic collection type
and then extracted, since the declared mode for the extraction will typically be ‘out’ and
the higher-order inst information will be lost. To partially alleviate this problem, and to
make higher-order functional programming easier, if the term to be called has a function
type, but no higher-order inst information, we assume that it has the default higher-order
function inst ‘func(in, ..., in) = out is Determinism ’.
Mercury also provides builtin ‘inst’ values for use with solver types:
any_pred is Determinism
any_pred(Mode ) is Determinism
any_pred(Mode1, Mode2 ) is Determinism
...
any_func = Mode is Determinism
any_func(Mode1 ) = Mode is Determinism
any_func(Mode1, Mode2 ) = Mode is Determinism
...
9 Modules
:- type queue(T).
:- pred empty_queue(queue(T)).
:- mode empty_queue(out) is det.
:- mode empty_queue(in) is semidet.
:- implementation.
:- import_module list.
empty_queue([]).
:- end_module queue.
9.3 Sub-modules
As mentioned above, modules may contain sub-modules. There are two kinds of sub-
modules, called nested sub-modules and separate sub-modules; the difference is that nested
sub-modules are defined in the same source file as the containing module, whereas separate
sub-modules are defined in separate source files. Implementations should support separate
compilation of separate sub-modules.
A module may not contain more than one sub-module with the same name.
Note that a sub-module for which the ‘:- module’ or ‘:- include_module’ declaration
occurs only in the implementation section of the parent module may only be imported or
used by its parent module or by sub-modules of its parent module.
Note that as mentioned previously, all ‘:- import_module’ and ‘:- use_module’ decla-
rations must use fully-qualified module names.
:- finalise finalpredname/arity.
where the predicate ‘finalpredname/arity’ must be declared with one of the following
signature:
:- pred finalpredname(io::di, io::uo) is Det.
:- impure pred finalpredname is Det
Det must be either ‘det’ or ‘cc_multi’.
The effect of the ‘finalise’ declaration is to ensure that ‘finalpredname/arity’ is
invoked after the program’s ‘main’ predicate. Finalisation predicates within a module are
executed in the order in which they are specified, although no order may be assumed between
different modules or sub-modules. Any finalisation required by the Mercury standard library
will always occur after any finalisation predicates have been invoked.
If ‘finalpredname/arity’ terminates with an uncaught exception then the program will
immediately abort execution. No predicates specified by other ‘finalise’ directives that
have not yet been executed will be executed.
‘finalize’ is also allowed as a synonym for ‘finalise’.
Note: ‘finalise’ declarations are currently only supported on the C, Java and Erlang
backends.
‘attach_to_io_state’
This attribute causes the compiler to also construct access predicates that have
the following signatures:
:- pred get_varname(vartype::out(varinst), io::di, io::uo) is det.
:- pred set_varname(vartype::in(varinst), io::di, io::uo) is det.
‘constant’
This attribute causes the compiler to construct only a ‘get’ access predicate,
but not a ‘set’ access predicate. Since ‘varname’ will always have the initial
value given to it, the ‘get’ access predicate is pure; its signature will be:
:- pred get_varname(vartype::out(varinst)) is det.
The ‘constant’ attribute cannot be specified together with the
‘attach_to_io_state’ attribute (since they disagree on this signature). It
also cannot be specified together with an explicit ‘trailed’ attribute.
The Melbourne Mercury compiler also supports the following attributes:
‘foreign_name(Lang, Name)’
Allow foreign code to access the mutable variable in some implementation de-
pendent manner. ‘Lang’ must be a valid target language for this Mercury
implementation. ‘Name’ must be a valid identifier in that language. It is an
error to specify more than one foreign name attribute for each language.
For the C backends this attribute allows foreign code to access the mutable
variable as an external variable called ‘Name’. For the low-level C backend,
e.g. the asm fast grades, the type of this variable will be MR_Word. For the
high-level C backend, e.g. the hlc grades, the type of this variable depends
upon the Mercury type of the mutable. For mutables of the Mercury types
int, float, char and string, the corresponding C types will be MR_Integer,
MR_Float, MR_Char and MR_String respectively. For mutables of any other
type the corresponding C type will be MR_Word.
This attribute is not currently implemented for the non-C backends.
‘thread_local’
This attribute allows a mutable to take on different values in each thread. When
a child thread is spawned, it inherits all the values of thread-local mutables of
the parent thread. Changing the value of a thread-local mutable does not affect
its value in any other threads.
The ‘thread_local’ attribute cannot be specified together with either of the
‘trailed’ or ‘constant’ attributes.
It is an error for a ‘mutable’ directive to appear in the interface section of a module.
The usual visibility rules for sub-modules apply to the mutable variable access predicates.
For the purposes of determining when mutables are assigned their initial values, the ex-
pression ‘initial_value’ behaves as though it were a predicate specified in an ‘initialise’
directive.
:- initialise foo/2.
:- mutable(bar, int, 561, ground, [untrailed]).
:- initialise baz/2.
Chapter 10: Type classes 65
In the above example ‘foo/2’ is invoked first, then ‘bar’ is set with an initial value of
561 and the ‘baz/2’ is invoked.
The effect of a mutable initial value expression terminating with an uncaught exception
is also the same as though it were a predicate specified in a ‘initialise’ directive.
Note: ‘mutable’ declarations are currently only supported on the C, Java and Erlang
backends.
10 Type classes
Mercury supports constrained polymorphism in the form of type classes. Type classes allow
the programmer to write predicates and functions which operate on variables of any type
(or sequence of types) for which a certain set of operations is defined.
declaration get the usual default mode (see Chapter 4 [Modes], page 34): all arguments have
mode ‘in’, the result has mode ‘out’, and the determinism is ‘det’.
The number of parameters to the type class (e.g. T) is not limited. For example, the
following is allowed:
:- typeclass a(T1, T2) where [...].
The parameters must be distinct variables. Each typeclass declaration must have at
least one parameter.
It is OK for a typeclass declaration to declare no methods, e.g.
:- typeclass foo(T) where [].
There must not be more than one type class declaration with the same name and arity
in the same module.
so they can be facts, rules, or DCG rules. The only difference is that in instance declarations,
clauses are separated by commas rather than being terminated by periods, and so rules and
DCG rules in instance declarations must normally be enclosed in parentheses. As with
ordinary predicates, you can have more than one clause for each method. The clauses must
satisfy the declared type, modes, determinism and purity for the method, after the types of
the arguments in the instance declaration have been substituted in place of the parameters
in the type class declaration.
These two ways are mutually exclusive: each method must be defined either by a single
naming definition (using the ‘pred(...) is predname ’ or ‘func(...) is funcname ’ form),
or by a set of one or more clauses, but not both.
Here’s an example of an instance declaration and the different kinds of method definitions
that it can contain:
:- typeclass foo(T) where [
func method1(T, T) = int,
func method2(T) = int,
pred method3(T::in, int::out) is det,
pred method4(T::in, io.state::di, io.state::uo) is det,
func method5(bool, T) = T
].
Note that even if a type class has no methods, an explicit instance declaration is required
for a type to be considered an instance of that type class.
Here’s an example of some code using an instance declaration:
:- type coordinate
---> coordinate(
float, % X coordinate
float % Y coordinate
).
:- type coloured_coordinate
---> coloured_coordinate(
float,
float,
rgb
).
:- instance hashable(int).
:- instance hashable(string).
:- implementation.
:- end_module hashable.
distance(A, B, Distance) :-
coords(A, Xa, Ya),
coords(B, Xb, Yb),
XDist = Xa - Xb,
YDist = Ya - Yb,
Distance = sqrt(XDist*XDist + YDist*YDist).
In the above example, the distance predicate is able to calculate the distance between
any two points, regardless of their representation, as long as the coords operation has been
defined. These constraints are checked at compile time.
Chapter 10: Type classes 71
portray_list([], !IO).
portray_list([X | Xs], !IO) :-
portray(X, !IO),
io.write_char(’ ’, !IO),
portray_list(Xs, !IO).
For abstract instance declarations, the type class constraints on an abstract instance
declaration must exactly match the type class constraints on the corresponding non-abstract
instance declaration that defines that instance.
:- typeclass Typeclass (Var, ...) <= (D1, D2, ... -> R1, R2, ...), ...
Each type variable must appear in the parameter list of the typeclass. Abstract typeclass
declarations must have exactly the same functional dependencies as in the implementation.
Mutually recursive functional dependencies are allowed, so the following examples are
legal:
:- typeclass foo(A, B) <= ((A -> B), (B -> A)).
:- typeclass bar(A, B, C, D) <= ((A, B -> C), (B, C -> D), (D -> A, C)).
A functional dependency on a typeclass places an additional requirement on the set of
instances which are allowed for that type class. The requirement is that all types bound to
variables in the range of the functional dependency must be able to be uniquely determined
by the types bound to variables in the domain of the functional dependency. If more than
one functional dependency is present, then the requirement for each one must be satisfied.
For example, given the typeclass declaration
:- typeclass baz(A, B) <= (A -> B) where ...
Chapter 10: Type classes 73
of constraints also induce functional dependencies on the variables, and the closure that we
calculate takes these into account.
For example, in this code
:- typeclass quux(P, Q, R) <= baz(R, P) where ...
11 Existential types
Existentially quantified type variables (or simply “existential types” for short) are useful
tools for data abstraction. In combination with type classes, they allow you to write code
in an “object oriented” style that is similar to the use of interfaces in Java or abstract base
classes in C++.
Mercury supports existential type quantifiers on predicate and function declarations,
and in data type definitions. You can put type class constraints on existentially quantified
type variables.
% Here the type variables ‘T1’ and ‘T2’ are existentially quantified.
:- some [T1, T2] func bar(int, list(T1), set(T2)) = pair(T1, T2).
Chapter 11: Existential types 75
:- pred foo(T).
foo(_).
% ok
:- pred call_foo.
call_foo :- foo(42).
% ok (T = int)
:- pred call_e_foo.
call_e_foo :- e_foo(_).
% ok
:- pred bad_foo(T).
bad_foo(42).
% type error
:- pred bad_call_e_foo.
bad_call_e_foo :- e_foo(42).
% type error
Existential constraints must only constrain type variables that are explicitly existentially
quantified. Likewise, universal constraints must only constrain type variables that are
universally quantified, although in this case the quantification does not have to be explicit
because universal quantification is the default (see Section 11.1.1 [Syntax for explicit type
quantifiers], page 74).
all possible values of the existentially quantified type variables which satisfy the declared
type class constraints.
In order to make this distinction clear to the compiler, whenever you want to construct
a value using an existentially quantified functor, you must prepend ‘new ’ onto the functor
name. This tells the compiler to treat it as though it were universally quantified: the caller
can bind that functor’s existentially quantified type variables to any type which satisfies
the declared type class constraints. Conversely, any occurrence without the ‘new ’ prefix
must be a deconstruction, and is therefore existentially quantified: the caller must not bind
the existentially quantified type variables, but the caller is allowed to depend on those type
variables satisfying the declared type class constraints, if any.
For example, the function ‘make_list’ constructs a value of type ‘list_of_showable’
containing a sequence of values of different types, all of which are instances of the ‘showable’
class
:- instance showable(int).
:- instance showable(float).
:- instance showable(string).
12 Exception handling
Mercury procedures may throw exceptions. Exceptions may be caught using the predicates
defined in the ‘exception’ library module, or using try goals.
A ‘try’ goal has the following form:
try Params Goal
then ThenGoal
else ElseGoal
catch Term -> CatchGoal
...
catch_any CatchAnyVar -> CatchAnyGoal
Goal, ThenGoal, ElseGoal, CatchGoal, CatchAnyGoal must be valid goals.
Goal must have one of the following determinisms: ‘det’, ‘semidet’, ‘cc_multi’, or
‘cc_nondet’.
The non-local variables of Goal must not have an inst equivalent to ‘unique’ or
‘mostly_unique’ or ‘any’, unless they have the type ‘io.state’.
Params must be a valid list of zero or more try parameters.
The “then” part is mandatory. The “else” part is mandatory if Goal may fail; otherwise
it must be omitted. There may be zero or more “catch” branches. The “catch any” part is
optional. CatchAnyVar must be a single variable.
The try parameter ‘io’ takes a single argument, which must be the name of a state
variable prefixed by ‘!’; for example, ‘io(!IO)’. The state variable must have the type
‘io.state’, and be in scope of the try goal. The state variable is threaded through ‘Goal’,
Chapter 12: Exception handling 82
so it may perform I/O but cannot fail. If no ‘io’ parameter exists, ‘Goal’ may not perform
I/O and may fail.
A try goal has determinism ‘cc_multi’.
On entering a try goal, Goal is executed. If it succeeds without throwing an exception,
ThenGoal is executed. Any variables bound by Goal are visible in ThenGoal only. If Goal
fails, then ElseGoal is executed.
If Goal throws an exception, the exception value is unified with each of the Terms in the
“catch” branches in turn. On the first successful unification, the corresponding CatchGoal
is executed (and other “catch” and “catch any” branches ignored). Variables bound during
the unification of the Term are in scope of the corresponding CatchGoal.
If the exception value does not unify with any of the terms in “catch” branches, and a
“catch any” branch is present, the exception is bound to CatchAnyVar and the CatchAny-
Goal executed. CatchAnyVar is visible in the CatchAnyGoal only, and is existentially
typed, i.e. it has type ‘some [T] T’.
Finally, if the thrown value did not unify with any “catch” term, and there is no
“catch any” branch, the exception is rethrown.
The declarative semantics of a try goal is:
(try [] Goal
then Then
else Else
catch CP1 -> CG1
catch CG2 -> CG2
...
catch_any CAV -> CAG
) <=>
(
Goal, Then
;
not Goal, Else
;
some [Excp]
( Excp = CP1 -> CG1
; Excp = CP2 -> CG2
; ...
; Excp = CAV, CAG
)
).
If no ‘else’ branch is present, let ‘Else = fail’. If no ‘catch_any’ branch is present, let
‘CAG = fail’.
An example of a try goal that performs I/O is:
:- pred p_carefully(io::di, io::uo) is det.
p_carefully(!IO) :-
(try [io(!IO)] (
io.write_string("Calling p\n", !IO),
Chapter 13: Semantics 83
p(Output, !IO)
)
then
io.write_string("p returned: ", !IO),
io.write(Output, !IO),
io.nl(!IO)
catch S ->
io.write_string("p threw a string: ", !IO),
io.write_string(S, !IO),
io.nl(!IO)
catch 42 ->
io.write_string("p threw 42\n", !IO)
catch_any Other ->
io.write_string("p threw something: ", !IO),
io.write(Other, !IO),
% Rethrow the value.
throw(X)
).
13 Semantics
A legal Mercury program is one that complies with the syntax, type, mode, determinism,
and module system rules specified in earlier chapters. If a program does not comply with
those rules, the compiler must report an error.
For each legal Mercury program, there is an associated predicate calculus theory whose
language is specified by the type declarations in the program and whose axioms are the
completion of the clauses for all predicates in the program, plus the usual equality axioms
extended with the completion of the equations for all functions in the program, plus axioms
corresponding to the mode-determinism assertions (see Chapter 6 [Determinism], page 42),
plus axioms specifying the semantics of library predicates and functions. The declarative
semantics of a legal Mercury program is specified by this theory.
Mercury implementations must be sound: the answers they compute must be true in
every model of the theory. Mercury implementations are not required to be complete: they
may fail to compute an answer in finite time, or they may exhaust the resource limitations of
the execution environment, even though an answer is provable in the theory. However, there
are certain minimum requirements that they must satisfy with respect to completeness.
There is an operational semantics of Mercury programs called the strict sequential op-
erational semantics. In this semantics, the program is executed top-down, starting from
‘main/2’ preceded by any module initialisation goals (as per Section 9.4 [Module initial-
isation], page 62), followed by any module finalisation goals (as per Section 9.5 [Module
finalisation], page 62), and function calls within a goal, conjunctions and disjunctions are
all executed in depth-first left-to-right order. Conjunctions and function calls are “mini-
mally” reordered as required by the modes: the order is determined by selecting the first
mode-correct sub-goal (conjunct or function call), executing that, then selecting the first
of the remaining sub-goals which is now mode-correct, executing that, and so on. (There
Chapter 13: Semantics 84
foreign language attributes specified below, or a promise pure or promise semipure pragma
as specified in Chapter 16 [Impurity], page 119).
Additional restrictions on the foreign language interface code depend on the foreign
language and compilation options. For more information, including the list of supported
foreign languages and the strings used to identify them, see the language specific information
in the “Foreign Language Interface” chapter of the Mercury User’s Guide.
If there is a pragma foreign_proc declaration for any mode of a predicate or function,
then there must be either a clause or a pragma foreign_proc declaration for every mode
of that predicate or function.
Here’s an example of code using ‘pragma foreign_proc’: The following code defines a
Mercury function ‘sin/1’ which calls the C function ‘sin()’ of the same name.
:- func sin(float) = float.
:- pragma foreign_proc("C", sin(X::in) = (Sin::out),
[may_call_mercury],
"Sin = sin(X);").
If the foreign language code does not recursively invoke Mercury code, as in the above
example, then you can use ‘will_not_call_mercury’ in place of ‘may_call_mercury’ in
the declarations above. This allows the compiler to use a slightly more efficient calling
convention. (If you use this form, and the C code does invoke Mercury code, then the
behaviour is undefined — your program may misbehave or crash.)
If there are both Mercury definitions and foreign proc definitions for a procedure and/or
foreign proc definitions for different languages, it is implementation defined which definition
is used.
For pure and semipure procedures, the declarative semantics of the foreign proc defini-
tions must be the same as that of the Mercury code. The only thing that is allowed to differ
is the efficiency (including the possibility of non-termination) and the order of solutions.
‘thread_safe’/‘not_thread_safe’/‘maybe_thread_safe’
This attribute declares whether or not it is safe for multiple threads to execute
this foreign language code concurrently. The default, in case none is specified,
is ‘not_thread_safe’. If the foreign language code is declared ‘thread_safe’,
then the Mercury implementation is permitted to execute the code concurrently
from multiple threads without taking any special precautions. If the foreign lan-
guage code is declared ‘not_thread_safe’, then the Mercury implementation
must not invoke the code concurrently from multiple threads. If the Mercury
implementation does use multithreading, then it must take appropriate steps to
prevent this. (The experimental multithreaded version of the current University
of Melbourne Mercury implementation protects ‘not_thread_safe’ code using
a mutex: C code that is not thread-safe has code inserted around it to obtain
and release a mutex. All non-thread-safe foreign language code shares a single
mutex.) If the foreign language code is declared ‘maybe_thread_safe’ then
whether the code is considered ‘thread_safe’ or ‘not_thread_safe’ depends
upon a compiler flag. This attribute is useful when the thread safety of the
foreign code itself is conditional. The Melbourne Mercury compiler uses the
‘--maybe-thread-safe’ option to set the value of the ‘maybe_thread_safe’
attribute.
Additional attributes which are supported by the Melbourne Mercury compiler are as
follows:
‘tabled_for_io’
This attribute should be attached to foreign procedures that do I/O. It tells
the debugger to make calls to the foreign procedure idempotent. This allows
the debugger to safely retry across such calls and also allows safe declarative
debugging of code containing such calls. For more information see the I/O
tabling section of the Mercury user guide. If the foreign procedure contains
gotos or static variables then the ‘pragma no_inline’ directive should also be
given (see Section 15.1.2 [pragma c code], page 111). Note that currently I/O
tabling will only be done for foreign procedures that take a pair of I/O state
arguments. Impure foreign procedures that perform I/O will not be made
idempotent, even if the tabled for io attribute is present. Note also that the
tabled for io attribute will likely be replaced in a future release with a more
general solution.
‘terminates’/‘does_not_terminate’
This attribute specifies the termination properties of the given predicate
or function definition. It is equivalent to the corresponding ‘pragma
terminates’ or ‘pragma does_not_terminate’ declaration. If omitted, the
termination property of the procedure is determined by the value of the
‘may_call_mercury’/‘will_not_call_mercury’ attribute. See Section 19.3
[Termination analysis], page 134 for more details.
‘max_stack_size(Size)’
This attribute declares the maximum stack usage of a particular piece of code.
The unit that ‘Size’ is measured in depends upon foreign language being used.
Chapter 14: Foreign language interface 88
Currently this attribute is only used (and is in fact required) by the ‘IL’ foreign
language interface, and is measured in units of stack items.
‘will_not_throw_exception’
This attribute promises that the given predicate or function will not make calls
back to Mercury that may result in an exception being thrown. It is an error
to apply this attribute to procedures that have determinism erroneous. This
attribute is ignored for code that is declared as not making calls back to Mercury
via the ‘will_not_call_mercury’ attribute. Note: predicates or functions that
have polymorphic arguments but do not explicitly throw an exception, via a
call to exception.throw/1 or require.error/1, may still throw exceptions because
they may be called with arguments whose types have user-defined equality or
comparison predicates. If these user-defined equality or comparison predicates
throw exceptions then unifications or comparisons involving these types may
also throw exceptions. As such, we recommend that only implementors of the
Mercury system use this annotation for polymorphic predicates and functions.
‘will_not_modify_trail/may_modify_trail’
This attribute declares whether or not a foreign procedure modifies the trail (see
Section 19.5 [Trailing], page 136). Specifying that a foreign procedure will not
modify the trail may allow the compiler to generate more efficient code for that
procedure. In compilation grades that do not support trailing this attribute is
ignored. The default, in case none is specified, is ‘may_modify_trail’.
‘will_not_call_mm_tabled/may_call_mm_tabled’
This attribute declares whether or not a foreign procedure makes calls back
to Mercury procedures that are evaluated using minimal model tabling (see
Section 19.2 [Tabled evaluation], page 130). Specifying that a foreign procedure
will not call procedures evaluated using minimal model tabling may allow the
compiler to generate more efficient code. In compilation grades that do not sup-
port minimal model tabling this attribute is ignored. These attributes may not
be used with procedures that do not make calls back to Mercury, i.e. that have
the ‘will_not_call_mercury’ attribute. The default for foreign procedures
that ‘may_call_mercury’, in case none is specified, is ‘may_call_mm_tabled’.
‘affects_liveness/does_not_affect_liveness’
This attribute declares whether or not a foreign procedure uses and/or modifies
any part of the Mercury virtual machine (registers, stack slots) through means
other than its arguments. The ‘affects_liveness’ attribute says that it does;
The ‘does_not_affect_liveness’ attribute says that it does not. In the ab-
sence of either attribute, the compiler assumes ‘affects_liveness’, unless the
code of the foreign proc in question is empty.
‘may_duplicate/may_not_duplicate’
This attribute tells the compiler whether it is allowed to duplicate the foreign
code fragment through optimizations such as inlining. The ‘may_duplicate’
attribute says that it may; The ‘may_not_duplicate’ attribute says that it
may not. In the absence of either attribute, the compiler is allowed make its
own judgement in the matter, based on factors such as the size of the code
fragment.
Chapter 14: Foreign language interface 89
Mercury tuple types are passed as ‘MR_Tuple’, which in the current implementation is a
typedef for a pointer of type ‘void *’ if ‘--high-level-code’ is enabled, and a typedef for
‘MR_Word’ otherwise.
Mercury variables of any other type are passed as a ‘MR_Word’, which in the current
implementation is a typedef for an unsigned type whose size is the same size as a pointer.
(Note: it would in fact be better for each Mercury type to map to a distinct abstract type
in C, since that would be more type-safe, and thus we may change this in a future release.
We advise programmers who are manipulating Mercury types in C code to use typedefs
for each user-defined Mercury type, and to treat each such type as an abstract data type.
This is good style and it will also minimize any compatibility problems if and when we do
change this.)
Mercury lists can be manipulated by C code using the following macros, which are
defined by the Mercury implementation.
MR_list_is_empty(list) /* test if a list is empty */
MR_list_head(list) /* get the head of a list */
MR_list_tail(list) /* get the tail of a list */
MR_list_empty() /* create an empty list */
MR_list_cons(head,tail) /* construct a list with the given head and tail */
Note that the use of these macros is subject to some caveats (see Section 14.9.1.8 [Memory
management for C], page 101).
The implementation provides the macro ‘MR_word_to_float’ for converting a value of
type ‘MR_Word’ to one of type ‘MR_Float’, and the macro ‘MR_float_to_word’ for converting
a value of type ‘MR_Float’ to one of type ‘MR_Word’. These macros must be used to perform
these conversions since for some Mercury implementations ‘sizeof(MR_Float)’ is greater
than ‘sizeof(MR_Word)’.
The following fragment of C code illustrates the correct way to extract the head of a
Mercury list of floats.
MR_Float f;
f = MR_word_to_float(MR_list_head(list));
Omitting the call to ‘MR_word_to_float’ in the above example would yield incorrect
results for implementations where ‘sizeof(MR_Float)’ is greater than ‘sizeof(MR_Word)’.
will be passed as the CLR type ‘foo.bar.baz_1’. Note an extra namespace qualifier,
‘mercury’, will be prepended to the beginning of names residing in the Mercury standard
library. Mercury variables whose type is a Mercury equivalence type will be passed as the
representation of the right hand side of the equivalence type.
This mapping is subject to change and you should try to avoid writing code that relies
heavily upon a particular representation of Mercury terms.
Mercury arguments declared with input modes are passed by value to the IL or C#
function. For output arguments, the Mercury implementation will pass to the IL or C#
function a reference to the location in which to store the result; for example, a Mercury
output argument of type ‘int’ would map to a C# ‘ref int’ function parameter. (Note
that we map to ‘ref int’, not ‘out int’; for procedures that can fail, output arguments
only need to be set if the procedure succeeds.) If the Mercury procedure can fail, then its
IL or C# function should return a truth value of type ‘bool’ (i.e. ‘System.Bool’) indicating
success or failure: true indicates success, and false indicates failure. If the Mercury
procedure is a Mercury function that cannot fail, and the function result has an output
mode, then the IL function should return the Mercury function result value. Otherwise
the function result is appended as an extra argument. Arguments of type ‘io.state’ or
‘store.store(_)’ are not passed or returned at all. (The reason for this is that these types
represent mutable state, and in IL modifications to mutable state are done via side effects,
rather than argument passing.)
For example, the following Mercury type corresponds to the Java class that follows (some
implementation details elided):
:- type maybe(T)
---> yes(yes_field :: T)
; no.
Mercury lists can be manipulated by Java code using the following methods, which are
defined by the Mercury implementation.
boolean list.is_empty(List_1<E> list) // test if a list is empty
E list.det_head(List_1<E> list) // get the head of a list
List_1<E> list.det_tail(List_1<E> list) // get the tail of a list
List_1<E> list.empty_list() // create an empty list
<E, F extends E> List_1<E> list.cons(F head, List_1<E> tail)
// construct a list with
// the given head and tail
By default, the contents of ‘pragma foreign_decl’ declarations are also visible in the
same kinds of declarations in other modules that import the module containing the ‘pragma
foreign_decl’ declaration. This is because they may be required to make sense of types de-
fined using ‘pragma foreign_type’ and/or predicates defined using ‘pragma foreign_code’
in the containing module, and these may be visible in other modules, especially in the pres-
ence of intermodule optimization,
If you do not want the contents of a ‘pragma foreign_decl’ declaration to be visible in
foreign language code in other modules, you can use the following variant of the declaration:
:- pragma foreign_decl("Lang ", local, DeclCode ).
Note: currently only the C and Erlang backends support this variant of the ‘pragma
foreign_decl’ declaration.
The declarations for Mercury predicates or functions exported to a foreign language
using a ‘pragma foreign_export’ declaration are visible to foreign code in a ‘pragma
foreign_code’ or ‘pragma foreign_proc’ declaration of the same module and also in those
of any sub-modules. They are not visible to the foreign code in ‘pragma foreign_code’ or
‘pragma foreign_proc’ declarations in any other module. They can be made visible using
a declaration of the form:
:- pragma foreign_import_module("Lang ", ImportedModule ).
where ImportedModule is the name of the module containing the ‘pragma foreign_export’
declarations.
If Lang is "C" this is equivalent to
:- pragma foreign_decl("C", "#include ""ImportedModule.mh""").
where ‘ImportedModule.mh’ is the automatically generated header file containing the C
declarations for the predicates and functions exported to C.
‘pragma foreign_import_module’ should be used instead of the explicit
#include because ‘pragma foreign_import_module’ tells the implementation that
‘ImportedModule.mh’ must be built before the object file for the module containing the
‘pragma foreign_import_module’ declaration.
A cycle of ‘pragma foreign_import_module’, where the language is ‘"C#"’ or ‘"Java"’,
is not permitted.
Note that the Melbourne Mercury implementation often implicitly inserts ‘pragma
foreign_import_module’ declarations but programmers should not write code that
depends upon this behaviour; ‘pragma foreign_import_module’ declarations should
always be explicitly included if needed.
:- pragma foreign_proc("C",
string.contains_char(Str::in, Ch::in),
[will_not_call_mercury, promise_pure],
"SUCCESS_INDICATOR = (strchr(Str, Ch) != NULL);").
SUCCESS_INDICATOR should not be used other than as the target of an assignment. (For
example, it may be #defined to a register, so you should not try to take its address.)
Procedures whose determinism indicates that they cannot fail should not access SUCCESS_
INDICATOR.
Chapter 14: Foreign language interface 100
Arguments whose mode is input will have their values set by the Mercury implementation
on entry to the C code. If the procedure succeeds, the C code must set the values of all
output arguments. If the procedure fails, the C code need only set SUCCESS_INDICATOR to
false (zero).
The behaviour of a procedure defined using a ‘pragma foreign_proc’ declaration whose
body contains a ‘return’ statement is undefined.
When using no garbage collection, you must be careful not to retain pointers to memory
on the Mercury heap after Mercury has backtracked to before the point where that memory
was allocated. You must also avoid the use of the macros MR_list_empty() and MR_
list_cons(). (The reason for this is that they may access Mercury’s ‘MR_hp’ register,
which might not be valid in C code. Using them in the bodies of procedures defined
using ‘pragma foreign_proc’ with ‘will_not_call_mercury’ would probably work, but
we don’t advise it.) Instead, you can write Mercury functions to perform these actions and
use ‘pragma foreign_export’ to access them from C. This alternative method also works
with conservative garbage collection.
Future Mercury implementations may use non-conservative methods of garbage collec-
tion. For such implementations, it will be necessary to explicitly register pointers passed
to C with the garbage collector. The mechanism for doing this has not yet been decided
on. It would be desirable to provide a single memory management interface for use when
interfacing with other languages that can work for all methods of memory management,
but more implementation experience is needed before we can formulate such an interface.
:- pragma foreign_proc("C#",
string.contains_char(Str::in, Ch::in),
[will_not_call_mercury, promise_pure],
"SUCCESS_INDICATOR = (Str.IndexOf(Ch) != -1);").
C# code for procedures whose determinism indicates that they cannot fail should not access
SUCCESS_INDICATOR.
Chapter 14: Foreign language interface 103
Arguments whose mode is input will have their values set by the Mercury implementation
on entry to the C# code. If the procedure succeeds, the C# code must set the values of all
output arguments. If the procedure fails, the C# code need only set SUCCESS_INDICATOR
to false.
to pass in appropriate ‘type_info’ values corresponding to the types of the other argu-
ments passed. These ‘type_info’ arguments can be obtained using the Mercury ‘type_of’
function in the Mercury standard library module ‘type_desc’.
The Java code in a pragma foreign_proc declaration for a procedure whose deter-
minism indicates that it can fail must assign a value of type ‘boolean’ to the variable
‘SUCCESS_INDICATOR’. For example:
:- pred string.contains_char(string, character).
:- mode string.contains_char(in, in) is semidet.
:- pragma foreign_proc("Java",
string.contains_char(Str::in, Ch::in),
[will_not_call_mercury, promise_pure],
"SUCCESS_INDICATOR = (Str.IndexOf(Ch) != -1);").
Java code for procedures whose determinism indicates that they cannot fail should not refer
to the SUCCESS_INDICATOR variable.
Arguments whose mode is input will have their values set by the Mercury implementation
on entry to the Java code. With our current implementation, the Java code must set the
values of all output variables, even if the procedure fails (i.e. sets the ‘SUCCESS_INDICATOR’
variable to false).
:- pragma foreign_proc("Erlang",
contains_char(Str::in, Ch::in),
[will_not_call_mercury, promise_pure, thread_safe],
"SUCCESS_INDICATOR = (string:chr(Str, Ch) =/= 0)").
Arguments whose mode is input will have their values set by the Mercury implementation
on entry to the Erlang code. The Erlang code must set the values of all output variables,
even if the procedure fails (i.e. sets the ‘SUCCESS_INDICATOR’ variable to false).
-define(FOO, 42).
").
:- pred hello(io.state::di, io.state::uo) is det.
:- pragma foreign_proc("Erlang",
hello(_IO0::di, _IO::uo),
[will_not_call_mercury],
"
io:format(""FOO = ~w~n"", [?FOO])
").
15 C interface
This chapter documents the original C interface. It has been deprecated in favour of the of
the new foreign language interface documented in Chapter 14 [Foreign language interface],
page 85. Support for this interface will be removed in a future version.
imports a C function for use by Mercury. Pred or Func must specify the name of a previously
declared Mercury predicate or function, and Mode1, Mode2, . . . , and (for functions) Mode
must specify one of the modes of that predicate or function. There must be no clauses for
the specified Mercury procedure; instead, any calls to that procedure will be executed by
calling the C function named C Name. The Attributes argument is optional; if present, it
specifies properties of the given C function (see Section 15.1.4 [C code attributes], page 114).
For example, the following code imports the C function ‘cos()’ as the Mercury function
‘cos/1’:
:- func cos(float) = float.
:- pragma import(cos(in) = out, [will_not_call_mercury], "cos").
The interface to the C function for a given Mercury procedure is determined as follows.
Mercury types are converted to C types and passed according to the rules in Section 15.6
[Passing data to and from C], page 118.
If you use ‘pragma import’ for a polymorphically typed Mercury procedure, the compiler
will prepend one ‘type_info’ argument to the parameters passed to the C function for each
polymorphic type variable in the Mercury procedure’s type signature. The values passed in
these arguments will be the same as the values that would be obtained using the Mercury
‘type_of’ function in the Mercury standard library module ‘type_desc’. These values
may be useful in case the C function wishes to in turn call another polymorphic Mercury
procedure (see Section 15.4 [Calling Mercury code from C], page 116).
You may not give a ‘pragma import’ declaration for a procedure with determinism
‘nondet’ or ‘multi’. (It is however possible to define a ‘nondet’ or ‘multi’ procedure
using ‘pragma c_code’. See Section 15.1.3 [Nondet pragma c code], page 112.)
Here FirstCode, RetryCode, and CommonCode are all Mercury strings containing C code.
FirstCode will be executed whenever the Mercury procedure is called. RetryCode will
be executed whenever a given call to the procedure is re-entered on backtracking to find
subsequent solutions. The ‘common_code(CommonCode )’ argument is optional; if present,
CommonCode will be executed after each execution of FirstCode or RetryCode.
The code that is executed on each call or retry should finish by executing one of the
three macros ‘FAIL’, ‘SUCCEED’, or ‘SUCCEED_LAST’. The ‘FAIL’ macro indicates that the
call has failed; the call will not be retried. The ‘SUCCEED’ macro indicates that the call has
succeeded, and that there may be more solutions; the call may be retried on backtracking.
The ‘SUCCEED_LAST’ macro indicates that the call has succeeded, but that there are no more
solutions after this one; the call will not be retried.
LocalVars is a sequence of struct member declarations which are used to hold any state
which needs to be preserved in case of backtracking or passed between the different C code
fragments. The code fragments FirstCode, RetryCode, and CommonCode may use the
macro ‘LOCALS’, which is defined to be a pointer to a struct containing the fields specified
by LocalVars, to access this saved state.
Note RetryCode and CommonCode may not access the input variables — only FirstCode
should access the input variables. If RetryCode or CommonCode need to access any of the
input variables, then FirstCode should copy the values needed to the LocalVars.
The following example shows how you can use a state variable to keep track of the next
alternative to return.
%
% This example implements the equivalent of
% foo(X) :- X = 20 ; X = 10 ; X = 42 ; X = 99 ; fail.
%
:- pred foo(int).
:- mode foo(out) is multi.
:- pragma c_code(foo(X::out), [will_not_call_mercury, thread_safe],
local_vars("
int state;
"),
first_code("
LOCALS->state = 1;
"),
retry_code("
LOCALS->state++;
"),
common_code("
switch (LOCALS->state) {
case 1: X = 20; SUCCEED; break;
case 2: X = 10; SUCCEED; break;
case 3: X = 42; SUCCEED; break;
case 4: X = 99; SUCCEED; break;
case 5: FAIL; break;
}
")
Chapter 15: C interface 114
).
The next example is a more realistic example; it shows how you could implement the
reverse mode of ‘string.append’, which returns all possible ways of splitting a string into
two pieces, using ‘pragma c_code’.
:- pred string.append(string, string, string).
:- mode string.append(out, out, in) is multi.
:- pragma c_code(string.append(S1::out, S2::out, S3::in),
[will_not_call_mercury, thread_safe],
local_vars("
String s;
size_t len;
size_t count;
"),
first_code("
LOCALS->s = S3;
LOCALS->len = strlen(S3);
LOCALS->count = 0;
"),
retry_code("
LOCALS->count++;
"),
common_code("
S1 = copy_substring(LOCALS->s, 0, LOCALS->count);
S2 = copy_substring(LOCALS->s, LOCALS->count,
LOCALS->len);
if (LOCALS->count < LOCALS->len) {
SUCCEED;
} else {
SUCCEED_LAST;
}
")
).
‘thread_safe’/‘not_thread_safe’
This attribute declares whether or not it is safe for multiple threads to ex-
ecute this C code concurrently. The default, in case neither is specified, is
‘not_thread_safe’. If the C code is declared ‘thread_safe’, then the Mer-
cury implementation is permitted to execute the code concurrently from mul-
tiple threads without taking any special precautions. If the C code is declared
‘not_thread_safe’, then the Mercury implementation must not invoke the code
concurrently from multiple threads. If the Mercury implementation does use
multithreading, then it must take appropriate steps to prevent this. (The exper-
imental multithreaded version of the current University of Melbourne Mercury
implementation protects ‘not_thread_safe’ code using a mutex: C code that
is not thread-safe has code inserted around it to obtain and release a mutex.
All non-thread-safe C code shares a single mutex.)
For each Mercury module containing ‘pragma export’ declarations, the Mercury im-
plementation will automatically create a header file for that module which declares a C
function C Name() for each of the ‘pragma export’ declarations. Each such C function is
the C interface to the specified mode of the specified Mercury predicate or function.
The interface to a Mercury procedure is determined as follows. (The rules here are
just the converse of the rules for ‘pragma import’). Mercury types are converted to C
types according to the rules in Section 15.6 [Passing data to and from C], page 118. Input
arguments are passed by value. For output arguments, the caller must pass the address in
which to store the result. If the Mercury procedure can fail, then its C interface function
returns a truth value indicating success or failure. If the Mercury procedure is a Mercury
function that cannot fail, and the function result has an output mode, then the C interface
function will return the Mercury function result value. Otherwise the function result is
appended as an extra argument. Arguments of type ‘io.state’ or ‘store.store(_)’ are
not passed at all. (The reason for this is that these types represent mutable state, and in
C modifications to mutable state are done via side effects, rather than argument passing.)
Calling polymorphically typed Mercury procedures from C is a little bit more difficult
than calling ordinary (monomorphically typed) Mercury procedures. The simplest method
is to just create monomorphic forwarding procedures that call the polymorphic procedures,
and export them, rather than exporting the polymorphic procedures.
If you do export a polymorphically typed Mercury procedure, the compiler will prepend
one ‘type_info’ argument to the parameter list of the C interface function for each poly-
morphic type variable in the Mercury procedure’s type signature. The caller must arrange
to pass in appropriate ‘type_info’ values corresponding to the types of the other argu-
ments passed. These ‘type_info’ arguments can be obtained using the Mercury ‘type_of’
function in the Mercury standard library module ‘type_desc’.
It is an error to export Mercury procedures that have a determinism of multi or nondet
to C.
To use the C declarations produced for ‘pragma export’ declarations in C code within
a Mercury module, use a ‘pragma c_import_module’ declaration, for example
:- pragma c_import_module(imported_module).
This is equivalent to
:- pragma c_header_code("#include ""imported_module.h""").
but it tells the implementation that the object file for the module containing the ‘pragma
c_import_module’ declaration should not be built before ‘imported_module.h’ is built.
:- interface.
:- type complicated_c_structure.
:- implementation.
:- pragma c_header_code("
extern struct foo *init_struct(void);
extern struct foo *perform_calculation(int, struct foo *);
");
:- pragma c_code(initialise_complicated_structure(Structure::uo),
[may_call_mercury],
"Structure = init_struct();").
[may_call_mercury],
"Structure = perform_calculation(Value, Structure0);").
We strong recommend the use of ‘pragma foreign_type’ instead of c_pointer as the
use of ‘pragma foreign_type’ results in more type-safe code. (see Section 14.9.1.1 [Using
pragma foreign type for C], page 98.)
16 Impurity declarations
In order to efficiently implement certain predicates, it is occasionally necessary to venture
outside pure logic programming. Other predicates cannot be implemented at all within the
paradigm of logic programming, for example, all solutions predicates. Such predicates are
often written using the C interface. Sometimes, however, it would be more convenient, or
more efficient, to write such predicates using the facilities of Mercury. For example, it is
much more convenient to access arguments of compound Mercury terms in Mercury than
in C, and the ability of the Mercury compiler to specialize code can make higher-order
predicates written in Mercury significantly more efficient than similar C code.
One important aim of Mercury’s impurity system is to make the distinction between
the pure and impure code very clear. This is done by requiring every impure predicate or
function to be so declared, and by requiring every call to an impure predicate or function
to be flagged as such. Predicates or functions that are implemented in terms of impure
predicates or functions are assumed to be impure themselves unless they are explicitly
promised to be pure.
Please note that the facilities described here are needed only very rarely. The main
intent is for implementing language primitives such as the all solutions predicates, or for
implementing interfaces to foreign language libraries using the foreign language interface.
Any other use of ‘impure’ or ‘semipure’ probably indicates either a weakness in the Mer-
cury standard library, or the programmer’s lack of familiarity with the standard library.
Newcomers to Mercury are hence encouraged to skip this section.
semipure Semipure predicates are just like pure predicates, except that their declarative
semantics may be affected by the invocation of impure predicates. That is, they
are sensitive to the state of the computation other than as reflected by their
input arguments, though they do not affect the state themselves.
impure Impure predicates may perform I/O or modify hidden state, even if these side
effects alter the operational semantics of other code. However, impure predi-
cates may not change the declarative semantics of pure code. They must be
type-, mode-, determinism- and uniqueness correct.
16.3 Semantics
It is important to the proper operation of impure and semipure code, to the flexibility of
the compiler to optimize pure code, and to the semantics of the Mercury language, that
a clear distinction be drawn between ordinary Mercury code and imperative code written
with Mercury syntax. How this distinction is drawn will be explained below; the purpose
of this section is to explain the semantics of programs with impure predicates.
A declarative semantics of impure Mercury code would be largely useless, because the
declarative semantics cannot capture the intent of the programmer. Impure predicates are
executed for their side-effects, which by definition are not part of their declarative semantics.
Thus it is the operational semantics of impure predicates that Mercury must specify, and
Mercury compilers must respect.
The operational semantics of a Mercury predicate which invokes impure code is a mod-
ified form of the strict sequential semantics (see Chapter 13 [Semantics], page 83). Impure
goals may not be reordered relative to any other goals; not even “minimal” reordering as
implied by the modes is permitted. If any such reordering is needed, this is a mode error.
However, pure and semipure goals may be reordered as the compiler desires (within the
bounds of the semantics the user has specified for the program) as long as they are not
moved across an impure goal. Execution of impure goals is strict: they must be executed if
they are reached, even if it can be determined that the computation cannot lead to successful
termination.
Semipure goals can be given a “contextual” declarative semantics. They cannot have
any side-effects, so it is expected that, given the context in which they are called (relative
to any impure goals in the program), their declarative semantics fully captures the intent
of the programmer. Thus a semipure goal has a perfectly consistent declarative semantics,
until an impure goal is reached. After that, it has another (possibly different) declarative
semantics, until the next impure goal is executed, and so on. Mercury compilers must
respect this contextual nature of the semantics of semipure goals; within a single context,
a compiler may treat a semipure goal as if it were pure.
Chapter 16: Impurity declarations 121
"
max = INT_MIN;
").
:- pragma promise_pure(max_solution/2).
:- pred max_solution(pred(int), int).
:- mode max_solution(pred(out) is multi, out) is det.
max_solution(Generator, Max) :-
impure init_max,
(
Generator(X),
impure set_max(X),
fail
;
semipure Max = get_max
).
There are no implicit conversions and no subtyping relationship between ordinary higher-
order types and the corresponding impure or semipure higher-order types. However, a value
of an ordinary higher-order type can be explicit “converted” to a value of an impure (or
semipure) higher-order type by wrapping it in an impure (or semipure) lambda expression
that just calls the pure higher-order term.
foo(ImpurePred) = X1 + X2 :-
% using higher-order syntax
impure ImpurePred(X1),
% using the call/N syntax
impure call(ImpurePred, X2).
For calling impure or semipure higher-order functions, the notation is different than what
you might expect. In addition to using an ‘impure’ or ‘semipure’ operator on the unification
which invokes the higher-order function application, you must also use ‘impure_apply’ or
‘semipure_apply’ rather than using ‘apply’ or higher-order syntax. For example:
:- func map(impure func(T1) = T2, list(T1)) = list(T2).
17 Trace goals
Sometimes, programmers find themselves needing to perform some side-effects in the middle
of declarative code. One example is an operation that takes so long that users may think
the program has gone into an infinite loop: periodically printing a progress message can give
them reassurance. Another example is a program that is too long-running for its behaviour
to be analyzed via debuggers and too complex for analysis via profilers; a programmable
logging facility generating data for analysis by a specially-written program may be the best
option. However, inserting arbitrary side effects into declarative code is against the spirit of
Mercury. Trace goals exist to provide a mechanism to code these side effects in a disciplined
fashion.
The format of trace goals is trace Params Goal . Goal must be a valid goal; Params
must be a valid list of one or more trace parameters. The following example shows all four
of the available kinds of parameters: ‘compile_time’, ‘run_time’, ‘io’ and ‘state’. (In
practice, it is far more typical to have just one parameter, ‘io’.)
:- mutable(logging_level, int, 0, ground, []).
time_consuming_task(In, Out) :-
trace [
compile_time(flag("do_logging") or grade(debug)),
run_time(env("VERBOSE")),
io(!IO),
state(logging_level, !LoggingLevel)
] (
io.write_string("Time_consuming_task start\n", !IO),
( !.LoggingLevel > 1 ->
io.write_string("Input is ", !IO),
io.write(In, !IO),
io.nl(!IO)
;
true
)
),
...
% perform the actual task
The ‘compile_time’ parameter says under what circumstances the trace goal should be
included in the executable program. In the example, at least one of two conditions has to be
true: either this module has to be compiled with the option ‘--trace-flag=do_logging’,
or it has to be compiled in a debugging grade.
In general, the single argument of the ‘compile_time’ function symbol is a boolean
expression of primitive compile-time conditions. Valid boolean operators in these expres-
sions are ‘and’, ‘or’ and ‘not’. There are three kinds of primitive compile-time condi-
tions. The first has the form ‘flag(FlagName )’, where FlagName is an arbitrary name
Chapter 17: Trace goals 126
picked by the programmer; this condition is true if the module is compiled with the op-
tion ‘--trace-flag=FlagName ’. The second has the form ‘grade(debug)’; this condition
is true if the module is compiled in a debugging grade. (We may support the specification
of other kinds of grades in the future.) The third has the form ‘tracelevel(shallow)’, or
‘tracelevel(deep)’; this condition is true (irrespective of grade) if the module is compiled
with at least the specified trace level.
The ‘run_time’ parameter says under what circumstances the trace goal, if included in
the executable program, should actually be executed. In this case, the environment variable
‘VERBOSE’ has be to set when the program starts execution. (It doesn’t matter what value
it is set to.)
In general, the single argument of the ‘run_time’ function symbol is a boolean expres-
sion of primitive run-time conditions. Valid boolean operators in these expressions are
‘and’, ‘or’ and ‘not’. There is just one primitive run-time condition. It has the form
‘env(EnvVarName )’, this condition is true if the environment variable EnvVarName exists
when the program starts execution.
The ‘compile_time’ and ‘run_time’ parameters may not appear in the parameter list
more than once; programmers who want more than one condition have to specify how (with
what boolean operators) their values should be combined. However, it is ok for them not
to appear in the parameter list at all. If there is no ‘compile_time’ parameter, the trace
goal is always compiled into the executable; if there is no ‘run_time’ parameter, the trace
goal is always executed (if it is compiled into the executable).
Since the trace goal may end up either not compiled into the executable or just not
executed, it cannot bind any variables that occur in the surrounding code. (If it were allowed
to bind such variables, then those variables would stay unbound if either the compile time
or the run time condition were false.) This greatly restricts what trace goals can do.
The usual reason for including a trace goal in a procedure body is to perform some
I/O, which requires access to the I/O state. The ‘io’ parameter supplies this access. Its
argument must be the name of a state variable prefixed by ‘!’; by convention, it is usually
‘!IO’. The language implementation supplies the initial unique value of the I/O state as
the value of ‘!.IO’ at the start of the trace goal; it requires the trace goal to give back the
final unique value of the I/O state as the value of ‘!.IO’ current at the end of the trace
goal.
Note that trace goals that wish to do I/O must include this parameter in their parameter
list even if the surrounding code already has access to an I/O state. This is because
otherwise, doing any I/O inside the trace goal would destroy the value of the current I/O
state, changing the instantiation state of the variable holding it, and trace goals are not
allowed to do that.
The ‘io’ parameter may appear in the parameter list at most once, since it doesn’t make
sense to have two copies of the I/O state available to the trace goal.
Besides doing I/O, trace goals may read and possibly write the values of mutable vari-
ables. Each mutable the trace goal wants access to should be listed in its own ‘state’ pa-
rameter (which may therefore appear in the parameter list more than once). Each ‘state’
parameter has two arguments: the first gives the name of the mutable, while the second
must be the name of a state variable prefixed by ‘!’, e.g. ‘!LoggingLevel’. The lan-
guage implementation supplies the initial value of the mutable as the value of (in this case)
Chapter 18: Pragmas 127
‘!.LoggingLevel’ at the start of the trace goal; at the end of the trace goal, it puts the
value of ‘!.LoggingLevel’ current then back into the mutable.
The intention here is that trace goals should be able to access mutables that give them
information about the parameters within which they should operate. The ability of trace
goals to actually update the values of mutables is intended to allow the implementation
of trace goals whose actions depend on the actions executed by previous trace goals. For
example, a trace goal could test whether the current input value is the same as the previous
input value, and if it is, then it can say so instead of printing the value out again. Another
possibility is a progress message which is printed not for every item processed, but for every
1000th item, reassuring users without overwhelming them with a deluge of output.
This kind of code is the only intended use of this ability. Any program in which the value
of a mutable set by a trace goal is inspected by code that is not itself within a trace goal
is explicitly violating the intended uses of trace goals. Only the difficulty of implementing
the required global program analysis prevents the language design from outlawing such
programs in the first place.
The compiler will not delete trace goals from the bodies of the procedures containing
them. However, trace goals inside a procedure don’t prevent calls to that procedure from
being optimized away, if such optimization is otherwise possible. (There is no point in
debugging or logging operations that don’t actually occur.) In their effect on program
optimizations, trace goals function as a kind of impure code, but one with an implicit
promise pure around the clause in which they occur.
18 Pragmas
The pragma declarations described below are a standard part of the Mercury language, as
are the pragmas for controlling the C interface (see Chapter 15 [C interface], page 110) and
impurity (see Chapter 16 [Impurity], page 119). As an extension, implementations may
also choose to support additional pragmas with implementation-dependent semantics (see
Chapter 19 [Implementation-dependent extensions], page 130).
18.1 Inlining
A declaration of the form
:- pragma inline(Name /Arity ).
is a hint to the compiler that all calls to the predicate(s) or function(s) with name Name
and arity Arity should be inlined.
The current Mercury implementation is smart enough to inline simple predicates even
without this hint.
A declaration of the form
:- pragma no_inline(Name /Arity ).
ensures the compiler will not inline this predicate. This may be used simply for performance
concerns (inlining can cause unwanted code bloat in some cases) or to prevent possibly
dangerous inlining when using low-level C code.
Chapter 18: Pragmas 128
18.3 Obsolescence
A declaration of the form
:- pragma obsolete(Name /Arity ).
declares that the predicate(s) or function(s) with name Name and arity Arity are “obsolete”:
it instructs the compiler to issue a warning whenever the named predicate(s) or function(s)
are used.
‘pragma obsolete’ declarations are intended for use by library developers, to allow grad-
ual (rather than abrupt) evolution of library interfaces. If a library developer changes the
interface of a library predicate, they should leave the old version of that predicate in the
library, but mark it as obsolete using a ‘pragma obsolete’ declaration, and document how
library users should modify their code to suit the new interface. The users of the library will
then get a warning if they use obsolete features, and can consult the library documentation
to determine how to fix their code. Eventually, when the library developer deems that users
have had sufficient warning, they can remove the old version entirely.
19 Implementation-dependent extensions
The University of Melbourne Mercury implementation supports the following extensions to
the Mercury language:
if this situation is encountered, it can (at the programmer’s option) either throw an excep-
tion, or avoid the infinite loop by computing solutions using a “minimal model” semantics.
(Specifically, the minimal model computed by our implementation is the perfect model.)
The current Mercury implementation supports three different pragmas for tabling, to
cover these three cases: ‘loop_check’, ‘memo’, and ‘minimal_model’. The ‘loop_check’
pragma asks only for loop checking. With this pragma, the memo table will map each
distinct set of input arguments only to a single boolean saying whether a call with those
arguments is currently active or not; the pragma’s only effect is to cause the predicate to
throw an exception if this boolean says that the current call has the same arguments as one
of its ancestors, which indicates an infinite recursive loop. The ‘memo’ pragma asks for both
loop checking and memoization. With this pragma, the memo table will map each distinct
set of input arguments either to the set of results computed previously for those arguments,
or to an indication that the call is still active and thus those results are still being computed.
This predicate will thus look for infinite recursive loops (and throw an exception if and
when it finds one) but it will also record all its solutions in the memo table, and will avoid
recomputing solutions that are already available in the memo table. The ‘minimal_model’
pragma asks for the computation of a “minimal model” semantics. These differ from the
‘memo’ pragma in that the detection of what appears to be an infinite recursive loop is not
fatal. The implementation will consider the apparently infinitely recursive calls to fail if
the call concerned has no way of computing any solutions it hasn’t already computed and
recorded, and it does have such a way, then it switches the execution to explore those ways
before coming back to the apparently infinitely recursive call.
The syntax for each of these declarations is
:- pragma memo(Name /Arity ).
:- pragma memo(Name /Arity, [list of tabling attributes ]).
:- pragma loop_check(Name /Arity ).
:- pragma loop_check(Name /Arity, [list of tabling attributes ]).
:- pragma minimal_model(Name /Arity ).
:- pragma minimal_model(Name /Arity, [list of tabling attributes ]).
where Name/Arity specifies the predicate or function to which the declaration applies. The
declaration applies to all modes of the predicate and/or function named. At most one of
these declarations may be specified for any given predicate or function.
Programmers can also request the application of tabling only to one particular mode of
a predicate or function, via declarations such as these:
:- pragma memo(Name (in, in, out)).
:- pragma memo(Name (in, in, out), [list of tabling attributes ]).
:- pragma loop_check(Name (in, out)).
:- pragma loop_check(Name (in, out), [list of tabling attributes ]).
:- pragma minimal_model(Name (in, in, out, out)).
:- pragma minimal_model(Name (in, in, out, out), [list of tabling attributes ]).
Because all variants of tabling record the values of input arguments, and all except
‘loop_check’ also record the values of output arguments, you cannot apply any of these
pragmas to procedures whose arguments’ modes include any unique component.
The optional list of attributes allows programmers to control some aspects of the man-
agement of the memo table(s) of the procedure(s) affected by the pragma.
Chapter 19: Implementation-dependent extensions 132
The ‘allow_reset’ attribute asks the compiler to define a predicate that, when called,
resets the memo table. The name of this predicate will be “table reset for”, followed by the
name of the tabled predicate, followed by its arity, and (if the predicate has more than one
mode) by the mode number (the first declared mode is mode 0, the second is mode 1, and
so on). These three or four components are separated by underscores. The reset predicate
takes a di/uo pair of I/O states as arguments. The presence of these I/O state arguments
in the reset predicate, and the fact that tabled predicates cannot have unique arguments
together imply that a memo table cannot be reset while a call using that memo table is
active.
The ‘statistics’ attribute asks the compiler to define a predicate that, when
called, returns statistics about the memo table. The name of this predicate will be
“table statistics for”, followed by the name of the tabled predicate, followed by its arity,
and (if the predicate has more than one mode) by the mode number (the first declared
mode is mode 0, the second is mode 1, and so on). These three or four components are
separated by underscores. The statistics predicate takes three arguments. The second and
third are a di/uo pair of I/O states, while the first is an output argument that contains
information about accesses to and modifications of the procedure’s memo table, both since
the creation of the table, and since the last call to this predicate. The type of this argument
is defined in the file table builtin.m in the Mercury standard library. That module also
contains a predicate for printing out this information in a programmer-friendly format.
The remaining two attributes, ‘fast_loose’ and ‘specified’, control how arguments
are looked up in the memo table. The default implementation looks up the value of each
input argument, and thus requires time proportional to the number of function symbols in
the input arguments. This is the only implementation allowed for minimal model tabling,
but for predicates tabled with the ‘loop_check’ and ‘memo’ pragmas, programmers can also
choose some other tabling methods.
The ‘fast_loose’ attribute asks the compiler to generate code that looks up only the ad-
dress of each input argument in the memo table, which means that the time required is linear
only in the number of input arguments, not their size. The tradeoff is that ‘fast_loose’
does not recognize calls as duplicates if they involve input arguments that are logically
equal but are stored at different locations in memory. The following declaration calls for
this variant of tabling.
:- pragma memo(Name (in, in, in, out),
[allow_reset, statistics, fast_loose]).
The ‘specified’ attribute allows programmers to choose individually, for each input
argument, whether that argument should be looked up in the memo table by value or by
address, or whether it should be looked up at all:
:- pragma memo(Name (in, in, in, out), [allow_reset, statistics,
specified([value, addr, promise_implied, output])]).
The ‘specified’ attribute should have an argument which is a list, and this list should
contain one element for each argument of the predicate or function concerned (if a func-
tion, the last element is for the return value). For output arguments, the list element
should be ‘output’. For input arguments, the list element may be ‘value’, ‘addr’ or
‘promise_implied’. The first calls for tabling the argument by value, the second calls
for tabling the argument by address, and the third calls for not tabling the argument at
Chapter 19: Implementation-dependent extensions 133
all. This last course of action promises that any two calls that agree on the values of the
value-tabled input arguments and on the addresses of the address-tabled input arguments
will behave the same regardless of the values of the untabled input arguments. In most
cases, this will mean that the values of the untabled arguments are implied by the values
of the value-tabled arguments and the addresses of the address-tabled arguments, though
the promise can also be fulfilled if the table predicate or function does not actually use the
untabled argument for computing any of its output. (It is ok for it to use the untabled
argument to decide what exception to throw.)
If the tabled predicate or function has only one mode, then this declaration can also be
specified without giving the argument modes:
For more information on tabling, see K. Sagonas’s PhD thesis The SLG-WAM: A Search-
Efficient Engine for Well-Founded Evaluation of Normal Logic Programs. See [[4]], page 142.
The operational semantics of procedures with a ‘pragma minimal_model’ declaration cor-
responds to what Sagonas calls “SLGd resolution”.
In the general case, the execution mechanism required by minimal model tabling is quite
complicated, requiring the ability to delay goals and then wake them up again. The Mercury
implementation uses a technique based on copying relevant parts of the stack to the heap
when delaying goals. It is described in Tabling in Mercury: design and implementation by
Z. Somogyi and K. Sagonas, Proceedings of the Eight International Symposium on Practical
Aspects of Declarative Languages.
Please note: the current implementation of tabling does not support all the possible
compilation grades (see the “Compilation model options” section of the Mercury User’s
Guide) allowed by the Mercury implementation. In particular, minimal model tabling is
incompatible with high level code, the use of trailing, and accurate garbage collection.
Chapter 19: Implementation-dependent extensions 134
proving termination of other predicates or functions. This declaration affects not only the
predicate specified but also any other predicates that are mutually recursive with it.
:- pragma does_not_terminate(Name /Arity ).
This declaration may be used to inform the compiler that this predicate may not termi-
nate. This declaration affects not only the predicate specified but also any other predicates
that are mutually recursive with it.
:- pragma check_termination(Name /Arity ).
This pragma forces the compiler to prove termination of this predicate. If it cannot
prove the termination of the specified predicate or function then the compiler will quit with
an error message.
‘strict_sequential’
This feature specifies that a semantics that is equivalent to the strict sequential
operational semantics must be used.
‘conservative_gc’
This feature specifies that a module requires conservative garbage collection.
This feature is only checked when using the C backends It is ignored by the
non-C backends.
When a module containing a ‘pragma require_feature_set’ declaration is compiled,
the implementation checks to see that the specified features are supported by the compila-
tion model. It emits an error if they are not.
A ‘pragma require_feature_set’ may only occur in the implementation section of a
module.
A ‘pragma require_feature_set’ affects only the module in which it occurs; in partic-
ular it does not affect any sub-modules
If a module contains multiple ‘pragma require_feature_set’ declarations then the im-
plementation should emit an error if any of them specifies a feature that is not supported
by the compilation model.
19.5 Trailing
In certain compilation grades (see the “Compilation model options” section of the Mer-
cury User’s Guide), the University of Melbourne Mercury implementation supports trailing.
Trailing is a means of having side-effects, such as destructive updates to data structures,
undone on backtracking. The basic idea is that during forward execution, whenever you
perform a destructive modification to a data structure that may still be live on backtrack-
ing, you should record whatever information is necessary to restore it on a stack-like data
structure called the “trail”. Then, if a computation fails, and execution backtracks to before
those updates were performed, the Mercury runtime engine will traverse the trail back to
the most recent choice point, undoing all those updates.
The interface used is a set of C functions (which are actually implemented as macros)
and types. Typically these will be called from C code within ‘pragma foreign_proc’ or
‘pragma foreign_code’ declarations in Mercury code.
For an example of the use of this interface, see the module ‘extras/trailed_update/tr_array.m’
in the Mercury extras distribution.
trail entries for those updates will not necessarily be discarded, because in general they may
still be necessary in case we backtrack to a prior choice point.
• MR_trail_current_value()
Prototype:
void MR_trail_current_value(MR_Word *address );
Ensures that if future execution backtracks to the current choice point, the
value currently in address will be restored.
‘MR_trail_current_value(address )’ is equivalent to ‘MR_trail_value(address,
*address )’.
Note that address must be word aligned for both MR_trail_current_value() and MR_
trail_value(). (The address of Mercury data structures that have been passed to C via
the foreign language interface are guaranteed to be appropriately aligned.)
void MR_trail_function(
void (*untrail_func )(void *, MR_untrail_reason),
void *value
);
A call to ‘MR_trail_function(untrail_func, value )’ adds an entry to the
function trail. The Mercury implementation ensures that if future execution
ever backtracks to current choicepoint, or backtracks past the current choi-
cepoint to some earlier choicepoint, then (*untrail_func )(value, reason )
will be called, where reason will be ‘MR_undo’ if the backtracking was due to
a goal failing, ‘MR_exception’ if the backtracking was due to a goal throw-
ing an exception, or ‘MR_retry’ if the backtracking was due to the use of the
“retry” command in ‘mdb’, the Mercury debugger, or any similar user request
in a debugger. The Mercury implementation also ensures that if the current
choice point is pruned because execution commits to never backtracking to
it, then (*untrail_func )(value, MR_commit) will be called. It also ensures
that if execution requires that the current goal be solvable, then (*untrail_
func )(value, MR_solve) will be called. This happens in calls to solutions/2,
for example. (MR_commit is used for “hard” commits, i.e. when we commit to a
solution and prune away the alternative solutions; MR_solve is used for “soft”
commits, i.e. when we must commit to a solution but do not prune away all the
alternatives.)
MR gc is currently not used — it is reserved for future use.
Typically if the untrail func is called with reason being ‘MR_undo’, ‘MR_exception’, or
‘MR_retry’, then it should undo the effects of the update(s) specified by value, and then free
any resources associated with that trail entry. If it is called with reason being ‘MR_commit’
or ‘MR_solve’, then it should not undo the update(s); instead, it may check for floundering
(see the next section). In the ‘MR_commit’ case it may, in some cases, be possible to also free
resources associated with the trail entry. If it is called with anything else (such as ‘MR_gc’),
then it should probably abort execution with an error message.
Note that the address of the C function passed as the first argument of MR_trail_
function() must be word aligned.
The check for floundering can be implemented using the function trail, by simply calling
‘MR_trail_function()’ to add a function trail entry whenever you create a delayed goal,
and putting the appropriate check for floundering in the ‘MR_commit’ and ‘MR_solve’ cases
of your function. The Mercury extras distribution includes an example of this: see the
‘ML_var_untrail_func()’ function in the file ‘extras/trailed_update/var.m’.) If your
function does detect floundering, then it should print an error message and then abort
execution.
• MR_current_choicepoint_id()
Prototype:
MR_ChoicepointId MR_current_choicepoint_id(void);
MR_current_choicepoint_id() returns a value indicating the identity of the
most recent choice point; that is, the point to which execution would backtrack if
the current computation failed. The value remains meaningful if the choicepoint
is pruned away by a commit, but is not meaningful after backtracking past the
point where the choicepoint was created (since choicepoint ids may be reused
after backtracking).
• MR_null_choicepoint_id()
Prototype:
MR_ChoicepointId MR_null_choicepoint_id(void);
MR_null_choicepoint_id() returns a “null” value that is distinct from any
value ever returned by MR_current_choicepoint_id. (Note that MR_null_
choicepoint_id() is a macro that is guaranteed to be suitable for use as a
static initializer, so that it can for example be used to provide the initial value
of a C global variable.)
• MR_choicepoint_newer()
Prototype:
Chapter 19: Implementation-dependent extensions 140
:- type int_ref.
:- implementation.
typedef struct {
MR_ChoicepointId prev_choicepoint;
MR_Integer data;
} C_IntRef;
").
:- pragma foreign_proc("C",
new_int_ref(Ref::uo, Value::in),
[will_not_call_mercury, promise_pure],
"
C_Intref *x = malloc(sizeof(C_IntRef));
x->prev_choicepoint = MR_current_choicepoint_id();
x->data = Value;
Ref = x;
").
:- pragma foreign_proc("C",
update_int_ref(Ref0::mdi, Ref::muo, OldValue::out, NewValue::in),
[will_not_call_mercury, promise_pure],
"
C_IntRef *x = Ref0;
OldValue = x->data;
/*
** Update x->prev_choicepoint to indicate that
** x->data’s previous value has been trailed
Chapter 20: Bibliography 142
20 Bibliography
[1]
Chris Speirs, Zoltan Somogyi and Harald Sondergaard, Termination Analysis for Mercury.
In P. Van Hentenryck, editor, Static Analysis: Proceedings of the 4th International Sym-
posium, Lecture Notes in Computer Science. Springer, 1997. A longer version is available
for download from <http://www.cs.mu.oz.au/publications/tr db/mu 97 09.ps.gz>.
[2]
Gerhard Groger and Lutz Plumer, Handling of mutual recursion in automatic termination
proofs for logic programs. In K. Apt, editor, The Proceedings of the Joint International
Conference and Symposium on Logic Programming, pages 336–350. MIT Press, 1992.
[3]
Chris Speirs, Termination Analysis for Logic Programs, Technical Report 97/23, Depart-
ment of Computer Science, The University of Melbourne, Melbourne, Australia, 1997.
Available from <http://www.cs.mu.oz.au/mercury/papers/mu 97 23.ps.gz>.
[4]
K. Sagonas, The SLG-WAM: A Search-Efficient Engine for Well-Founded Evaluation of
Normal Logic Programs, PhD thesis, SUNY at Stony Brook, 1996. Available from
<http://www.cs.kuleuven.ac.be/~kostis/Thesis/thesis.ps.gz>.
[5]
B. Demoen and K. Sagonas, CAT: the copying approach to tabling, submitted for publica-
tion, Katholieke Universiteit Leuven, 1998.