mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-23 13:23:47 +00:00
Estimated hours taken: 220
Aditi update syntax, type and mode checking.
Change the hlds_goal for constructions in preparation for
structure reuse to avoid making multiple conflicting changes.
compiler/hlds_goal.m:
Merge `higher_order_call' and `class_method_call' into a single
`generic_call' goal type. This also has alternatives for the
various Aditi builtins for which type declarations can't
be written.
Remove the argument types field from higher-order/class method calls.
It wasn't used often, and wasn't updated by optimizations
such as inlining. The types can be obtained from the vartypes
field of the proc_info.
Add a `lambda_eval_method' field to lambda_goals.
Add a field to constructions to identify which RL code fragment should
be used for an top-down Aditi closure.
Add fields to constructions to hold structure reuse information.
This is currently ignored -- the changes to implement structure
reuse will be committed to the alias branch.
This is included here to avoid lots of CVS conflicts caused by
changing the definition of `hlds_goal' twice.
Add a field to `some' goals to specify whether the quantification
can be removed. This is used to make it easier to ensure that
indexes are used for updates.
Add a field to lambda_goals to describe whether the modes were
guessed by the compiler and may need fixing up after typechecking
works out the argument types.
Add predicate `hlds_goal__generic_call_id' to work out a call_id
for a generic call for use in error messages.
compiler/purity.m:
compiler/post_typecheck.m:
Fill in the modes of Aditi builtin calls and closure constructions.
This needs to know which are the `aditi__state' arguments, so
it must be done after typechecking.
compiler/prog_data.m:
Added `:- type sym_name_and_arity ---> sym_name/arity'.
Add a type `lambda_eval_method', which describes how a closure
is to be executed. The alternatives are normal Mercury execution,
bottom-up execution by Aditi and top-down execution by Aditi.
compiler/prog_out.m:
Add predicate `prog_out__write_sym_name_and_arity', which
replaces duplicated inline code in a few places.
compiler/hlds_data.m:
Add a `lambda_eval_method' field to `pred_const' cons_ids and
`pred_closure_tag' cons_tags.
compiler/hlds_pred.m:
Remove type `pred_call_id', replace it with type `simple_call_id',
which combines a `pred_or_func' and a `sym_name_and_arity'.
Add a type `call_id' which describes all the different types of call,
including normal calls, higher-order and class-method calls
and Aditi builtins.
Add `aditi_top_down' to the type `marker'.
Remove `aditi_interface' from type `marker'. Interfacing to
Aditi predicates is now handled by `generic_call' hlds_goals.
Add a type `rl_exprn_id' which identifies a predicate to
be executed top-down by Aditi.
Add a `maybe(rl_exprn_id)' field to type `proc_info'.
Add predicate `adjust_func_arity' to convert between the arity
of a function to its arity as a predicate.
Add predicates `get_state_args' and `get_state_args_det' to
extract the DCG state arguments from an argument list.
Add predicate `pred_info_get_call_id' to get a `simple_call_id'
for a predicate for use in error messages.
compiler/hlds_out.m:
Write the new representation for call_ids.
Add a predicate `hlds_out__write_call_arg_id' which
replaces similar code in mode_errors.m and typecheck.m.
compiler/prog_io_goal.m:
Add support for `aditi_bottom_up' and `aditi_top_down' annotations
on pred expressions.
compiler/prog_io_util.m:
compiler/prog_io_pragma.m:
Add predicates
- `prog_io_util:parse_name_and_arity' to parse `SymName/Arity'
(moved from prog_io_pragma.m).
- `prog_io_util:parse_pred_or_func_name_and_arity to parse
`pred SymName/Arity' or `func SymName/Arity'.
- `prog_io_util:parse_pred_or_func_and_args' to parse terms resembling
a clause head (moved from prog_io_pragma.m).
compiler/type_util.m:
Add support for `aditi_bottom_up' and `aditi_top_down' annotations
on higher-order types.
Add predicates `construct_higher_order_type',
`construct_higher_order_pred_type' and
`construct_higher_order_func_type' to avoid some code duplication.
compiler/mode_util.m:
Add predicate `unused_mode/1', which returns `builtin:unused'.
Add functions `aditi_di_mode/0', `aditi_ui_mode/0' and
`aditi_uo_mode/0' which return `in', `in', and `out', but will
be changed to return `di', `ui' and `uo' when alias tracking
is implemented.
compiler/goal_util.m:
Add predicate `goal_util__generic_call_vars' which returns
any arguments to a generic_call which are not in the argument list,
for example the closure passed to a higher-order call or
the typeclass_info for a class method call.
compiler/llds.m:
compiler/exprn_aux.m:
compiler/dupelim.m:
compiler/llds_out.m:
compiler/opt_debug.m:
Add builtin labels for the Aditi update operations.
compiler/hlds_module.m:
Add predicate predicate_table_search_pf_sym, used for finding
possible matches for a call with the wrong number of arguments.
compiler/intermod.m:
Don't write predicates which build `aditi_top_down' goals,
because there is currently no way to tell importing modules
which RL code fragment to use.
compiler/simplify.m:
Obey the `cannot_remove' field of explicit quantification goals.
compiler/make_hlds.m:
Parse Aditi updates.
Don't typecheck clauses for which syntax errors in Aditi updates
are found - this avoids spurious "undefined predicate `aditi_insert/3'"
errors.
Factor out some common code to handle terms of the form `Head :- Body'.
Factor out common code in the handling of pred and func expressions.
compiler/typecheck.m:
Typecheck Aditi builtins.
Allow the argument types of matching predicates to be adjusted
when typechecking the higher-order arguments of Aditi builtins.
Change `typecheck__resolve_pred_overloading' to take a list of
argument types rather than a `map(var, type)' and a list of
arguments to allow a transformation to be performed on the
argument types before passing them.
compiler/error_util.m:
Move the part of `report_error_num_args' which writes
"wrong number of arguments (<x>; expected <y>)" from
typecheck.m for use by make_hlds.m when reporting errors
for Aditi builtins.
compiler/modes.m:
compiler/unique_modes.m:
compiler/modecheck_call.m:
Modecheck Aditi builtins.
compiler/lambda.m:
Handle the markers for predicates introduced for
`aditi_top_down' and `aditi_bottom_up' lambda expressions.
compiler/polymorphism.m:
Add extra type_infos to `aditi_insert' calls
describing the tuple to insert.
compiler/call_gen.m:
Generate code for Aditi builtins.
compiler/unify_gen.m:
compiler/bytecode_gen.m:
Abort on `aditi_top_down' and `aditi_bottom_up' lambda
expressions - code generation for them is not yet implemented.
compiler/magic.m:
Use the `aditi_call' generic_call rather than create
a new procedure for each Aditi predicate called from C.
compiler/rl_out.pp:
compiler/rl_gen.m:
compiler/rl.m:
Move some utility code used by magic.m and call_gen.m into rl.m.
Remove an XXX comment about reference counting being not yet
implemented - Evan has fixed that.
library/ops.m:
compiler/mercury_to_mercury.m:
doc/transition_guide.texi:
Add unary prefix operators `aditi_bottom_up' and `aditi_top_down',
used as qualifiers on lambda expressions.
Add infix operator `==>' to separate the tuples in an
`aditi_modify' call.
compiler/follow_vars.m:
Thread a `map(prog_var, type)' through, needed because
type information is no longer held in higher-order call goals.
compiler/table_gen.m:
Use the `make_*_construction' predicates in hlds_goal.m
to construct constants.
compiler/*.m:
Trivial changes to add extra fields to hlds_goal structures.
doc/reference_manual.texi:
Document Aditi updates.
Use @samp{pragma base_relation} instead of
@samp{:- pragma base_relation} throughout the Aditi documentation
to be consistent with other parts of the reference manual.
tests/valid/Mmakefile:
tests/valid/aditi_update.m:
tests/valid/aditi.m:
Test case.
tests/valid/Mmakefile:
Remove some hard-coded --intermodule-optimization rules which are
no longer needed because `mmake depend' is now run in this directory.
tests/invalid/*.err_exp:
Fix expected output for changes in reporting of call_ids
in typecheck.m.
tests/invalid/Mmakefile
tests/invalid/aditi_update_errors.{m,err_exp}:
tests/invalid/aditi_update_mode_errors.{m,err_exp}:
Test error messages for Aditi updates.
tests/valid/aditi.m:
tests/invalid/aditi.m:
Cut down version of extras/aditi/aditi.m to provide basic declarations
for Aditi compilation such as `aditi__state' and the modes
`aditi_di', `aditi_uo' and `aditi_ui'. Installing extras/aditi/aditi.m
somewhere would remove the need for these.
619 lines
21 KiB
Plaintext
619 lines
21 KiB
Plaintext
\input texinfo
|
|
@setfilename mercury_trans_guide.info
|
|
@settitle The Prolog to Mercury transition guide
|
|
|
|
@c --- texi2html doesn't support the @dir commands yet
|
|
@c @dircategory The Mercury Programming Language
|
|
@c @direntry
|
|
@c * Mercury Transition Guide: (mercury_trans_guide). The Prolog to Mercury Transition Guide.
|
|
@c @end direntry
|
|
|
|
@c @smallbook
|
|
@c @cropmarks
|
|
@finalout
|
|
@setchapternewpage off
|
|
@ifinfo
|
|
This file is an aid for people porting Prolog programs to Mercury.
|
|
|
|
Copyright (C) 1995-1999 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.
|
|
|
|
@ignore
|
|
Permission is granted to process this file through Tex and print the
|
|
results, provided the printed document carries copying permission
|
|
notice identical to this one except for the removal of this paragraph
|
|
(this paragraph not being relevant to the printed manual).
|
|
|
|
@end ignore
|
|
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 language, under the above conditions for modified versions.
|
|
@end ifinfo
|
|
|
|
@titlepage
|
|
@title The Prolog to Mercury transition guide
|
|
@author Thomas Conway
|
|
@author Zoltan Somogyi
|
|
@author Fergus Henderson
|
|
@page
|
|
@vskip 0pt plus 1filll
|
|
Copyright @copyright{} 1995-1999 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 language, under the above conditions for modified versions.
|
|
@end titlepage
|
|
@page
|
|
|
|
@ifinfo
|
|
@node Top,,, (mercury)
|
|
@top The Prolog to Mercury transition guide
|
|
|
|
This guide gives some advice about
|
|
translating Prolog programs into Mercury.
|
|
|
|
@menu
|
|
* Introduction:: Introduction
|
|
* Syntax:: Syntax
|
|
* IO:: Input and output
|
|
* FailLoops:: Failure driven loops, @code{assert} and @code{retract}
|
|
* Commits:: Cuts
|
|
* Accumulators:: Accumulators and difference lists
|
|
* Determinism:: Determinism
|
|
* All-solutions:: All-solutions predicates: @code{findall} and @code{setof}
|
|
@c * Problems:: Common Problems
|
|
@end menu
|
|
@end ifinfo
|
|
|
|
@node Introduction
|
|
@chapter Introduction
|
|
|
|
This document is intended to help the reader
|
|
translate existing Prolog programs to Mercury.
|
|
We assume that the reader is familiar with Prolog.
|
|
This guide should be used in conjunction with
|
|
the Mercury User's Guide and Reference Manuals.
|
|
|
|
If the Prolog code is quite declarative
|
|
and does not make use of Prolog's non-logical constructions,
|
|
the job of converting it to Mercury will usually be quite straight forward.
|
|
However, if the Prolog program makes extensive use of non-logical constructions,
|
|
conversion may be very difficult,
|
|
and a direct transliteration may be impossible.
|
|
Mercury code typically has a very different style to most Prolog code.
|
|
|
|
@node Syntax
|
|
@chapter Syntax and declarations
|
|
|
|
Prolog and Mercury have very similar syntax.
|
|
Although there are a few differences,
|
|
by and large if a program is accepted by a Prolog system,
|
|
it will be accepted by Mercury.
|
|
There are however a few extra operators defined by the Mercury term parser.
|
|
Here's a complete list of the operators in Mercury.
|
|
|
|
@example
|
|
OPERATOR ASSOCIATIVITY PRECEDENCE
|
|
* yfx 400
|
|
** xfy 300
|
|
+ yfx 500
|
|
+ fx 500
|
|
, xfy 1000
|
|
- yfx 500
|
|
- fx 500
|
|
---> xfy 1179
|
|
--> xfx 1200
|
|
-> xfy 1050
|
|
. xfy 600
|
|
/ yfx 400
|
|
// yfx 400
|
|
/\ yfx 500
|
|
:- xfx 1200
|
|
:- fx 1200
|
|
:: xfx 1175
|
|
; xfy 1100
|
|
< xfx 700
|
|
<< yfx 400
|
|
<= xfy 920
|
|
<=> xfy 920
|
|
= xfx 700
|
|
=< xfx 700
|
|
== xfx 700
|
|
==> xfx 1175
|
|
=> xfy 920
|
|
> xfx 700
|
|
>= xfx 700
|
|
>> yfx 400
|
|
\ fx 500
|
|
\+ fy 900
|
|
\/ yfx 500
|
|
\= xfx 700
|
|
^ xfy 200
|
|
~ fy 900
|
|
aditi_bottom_up fx 500
|
|
aditi_top_down fx 500
|
|
all fxy 950
|
|
and xfy 720
|
|
div yfx 400
|
|
else xfy 1170
|
|
end_module fx 1199
|
|
func fx 800
|
|
if fx 1160
|
|
import_module fx 1199
|
|
include_module fx 1199
|
|
impure fy 800
|
|
inst fx 1199
|
|
instance fx 1199
|
|
is xfx 701
|
|
lambda fxy 950
|
|
mod xfx 300
|
|
mode fx 1199
|
|
module fx 1199
|
|
not fy 900
|
|
or xfy 740
|
|
pragma fx 1199
|
|
pred fx 800
|
|
rule fx 1199
|
|
semipure fy 800
|
|
some fxy 950
|
|
then xfx 1150
|
|
type fx 1180
|
|
typeclass fx 1199
|
|
when xfx 900
|
|
where xfx 1175
|
|
@end example
|
|
|
|
In addition, Mercury implements both existential and universal quantification
|
|
using the syntax
|
|
|
|
@example
|
|
some Vars Goal
|
|
@end example
|
|
|
|
@noindent
|
|
and
|
|
|
|
@example
|
|
all Vars Goal
|
|
@end example
|
|
|
|
Mercury does not (yet) allow users to define their own operators.
|
|
|
|
@node IO
|
|
@chapter Input and output
|
|
|
|
Mercury is a purely declarative language.
|
|
Therefore it cannot use Prolog's mechanism for doing
|
|
input and output with side-effects.
|
|
The mechanism that Mercury uses is the threading of an object
|
|
that represents the state of the world through the computation.
|
|
The type of this structure is @samp{io__state}.
|
|
The modes of the two arguments that are added to calls are
|
|
@samp{di} for ``destructive input'' and @samp{uo} for ``unique output''.
|
|
The first means that the input variable
|
|
must be the last reference to the original state of the world,
|
|
and that the output variable will be the only reference
|
|
to the state of the world produced by this predicate.
|
|
|
|
Predicates that do input or output must have these arguments added.
|
|
For example the Prolog predicate:
|
|
|
|
@example
|
|
write_total(Total) :-
|
|
write('The total is '),
|
|
write(Total),
|
|
write('.'),
|
|
nl.
|
|
@end example
|
|
|
|
@noindent
|
|
in Mercury becomes
|
|
|
|
@example
|
|
:- pred write_total(int, io__state, io__state).
|
|
:- mode write_total(in, di, uo) is det.
|
|
|
|
write_total(Total, IO0, IO) :-
|
|
print("The total is ", IO0, IO1),
|
|
print(Total, IO1, IO2),
|
|
print('.', IO2, IO3),
|
|
nl(IO3, IO).
|
|
@end example
|
|
|
|
Definite Clause Grammars (DCGs) are convenient syntactic sugar
|
|
to use in such situations.
|
|
The above clause can also be written
|
|
|
|
@example
|
|
write_total(Total) -->
|
|
print("The total is "),
|
|
print(Total),
|
|
print('.'),
|
|
nl.
|
|
@end example
|
|
|
|
In DCGs, any calls (including unifications)
|
|
that do not need the extra DCG arguments
|
|
are escaped in the usual way by surrounding them in curly braces
|
|
(@code{ @{ @} }).
|
|
|
|
Note that in Mercury you normally use strings (@code{"..."})
|
|
rather than atoms (@code{'...'}) for messages like @code{"The total is"}.
|
|
(It's possible to use atoms, but you have to declare each
|
|
such atom before-hand, so its more convenient to use strings.)
|
|
However, for strings and characters, @samp{write} prints out the quotes;
|
|
to avoid this, you need to use @samp{print} instead of @samp{write}.
|
|
|
|
Both @samp{write} and @samp{print} are defined in the @samp{io}
|
|
module in the Mercury standard library.
|
|
|
|
One of the important consequences of our model for input and output
|
|
is that predicates that can fail may not do input or output.
|
|
This is because the state of the world must be a unique object,
|
|
and each IO operation destructively replaces it with a new state.
|
|
Since each IO operation destroys the current state object
|
|
and produces a new one,
|
|
it is not possible for IO to be performed in a context that may fail,
|
|
since when failure occurs the old state of the world will have been destroyed,
|
|
and since bindings cannot be exported from a failing computation,
|
|
the new state of the world is not accessible.
|
|
|
|
In some circumstances, Prolog programs that suffer from this problem
|
|
can be fixed by moving the IO out of the failing context.
|
|
For example
|
|
|
|
@example
|
|
...
|
|
( solve(Goal) ->
|
|
...
|
|
;
|
|
...
|
|
),
|
|
...
|
|
@end example
|
|
|
|
@noindent
|
|
where @samp{solve(Goal)} does some IO can be transformed into
|
|
valid Mercury in at least two ways. The first is to make
|
|
@samp{solve} deterministic and return a status:
|
|
|
|
@example
|
|
...
|
|
solve(Goal, Result, IO6, IO7),
|
|
( Result = yes ->
|
|
...
|
|
;
|
|
...
|
|
),
|
|
...
|
|
@end example
|
|
|
|
The other way is to transform @samp{solve} so that all the input
|
|
and output takes place outside it:
|
|
|
|
@example
|
|
...
|
|
io__write_string("calling: ", IO6, IO7),
|
|
solve__write_goal(Goal, IO7, IO8),
|
|
( solve(Goal) ->
|
|
io__write_string("succeeded\n", IO8, IO9),
|
|
...
|
|
;
|
|
IO9 = IO8,
|
|
...
|
|
),
|
|
...
|
|
@end example
|
|
|
|
@node FailLoops
|
|
@chapter Failure driven loops, assert and retract
|
|
|
|
Because Mercury is purely declarative,
|
|
the goal @samp{Goal, fail} is interchangeable with the goal @samp{fail, Goal}.
|
|
Also because it is purely declarative, there are no side effects to goals
|
|
(see also the section on input and output).
|
|
As a consequence of these two facts,
|
|
it is not possible to write failure driven loops in Mercury.
|
|
Neither is it possible to use predicates such as assert or retract.
|
|
This is not the place to argue it, but we believe
|
|
most programs that use failure driven loops, assert and retract
|
|
to be less clear and harder to maintain than those that do not.
|
|
|
|
The use of assert and retract should be replaced with
|
|
a collection data structure threaded through the relevant part of the program.
|
|
Data which is truly global may be stored in the @samp{io__state} using
|
|
the predicates @samp{io__get_globals} and @samp{io__set_globals}.
|
|
These predicates take an argument of type @samp{univ}, the universal
|
|
type, so that by using @samp{type_to_univ} and @samp{univ_to_type} it
|
|
is possible to store data of any type in the @samp{io__state}.
|
|
|
|
The standard library contains
|
|
several abstract data types for storing collections,
|
|
each of which will be useful for different classes of problems.
|
|
|
|
The @samp{list} ADT is useful if the order of the asserted facts is important.
|
|
The @samp{set} ADT is useful if the order is not important,
|
|
and if the asserted facts are not key-value pairs.
|
|
If the asserted facts are key-value pairs,
|
|
you can choose among several ADTs,
|
|
including @samp{map}, @samp{bintree}, @samp{rbtree}, and @samp{tree234}.
|
|
We recommend the @samp{map} ADT for generic use.
|
|
Its current implementation is as a 234 tree (using @samp{tree234}),
|
|
but in the future it may change to a hash table, or a trie,
|
|
or it may become a module that chooses among several implementation methods
|
|
dynamically depending on the size and characteristics of the data.
|
|
|
|
Failure driven loops in Prolog programs
|
|
should be transformed into ordinary tail recursion in Mercury.
|
|
This does have the disadvantage
|
|
that the heap space used by the failing clause
|
|
is not reclaimed immediately but only through garbage collection,
|
|
but we are working on ways to fix this problem.
|
|
In any case, the transformed code is more declarative
|
|
and hence easier to maintain and understand for humans
|
|
and easier for the compiler to optimize.
|
|
|
|
@node Commits
|
|
@chapter Cuts and indexing
|
|
|
|
The cut operator is not part of the Mercury language.
|
|
The builtin library does contain a predicate !/0 (and !/2 for DCGs),
|
|
but it is just defined as being identical to @samp{true},
|
|
and is there primarily for historical reasons@footnote{
|
|
The Mercury compiler was originally bootstrapped using
|
|
NU-Prolog and SICStus Prolog. We needed to use cuts for
|
|
efficiency in a few places. Of course, now that we compile
|
|
the Mercury compiler with itself the cuts are not needed
|
|
--- and it runs much faster anyway.}.
|
|
In addition, the conditional operator @samp{-> ;}
|
|
does not do a hard cut across the condition
|
|
- only a soft cut which prunes away either the `then' goal or the `else' goal.
|
|
If there are multiple solutions to the condition,
|
|
they will all be found on backtracking.
|
|
|
|
Prolog programs that use cuts and a `catch-all' clause should be
|
|
transformed to use if-then-else.
|
|
|
|
For example
|
|
|
|
@example
|
|
p(this, ...) :- !,
|
|
...
|
|
p(that, ...) :- !,
|
|
...
|
|
p(Thing, ...) :-
|
|
...
|
|
@end example
|
|
|
|
@noindent
|
|
should be rewritten as
|
|
|
|
@example
|
|
p(Thing, ...) :-
|
|
( Thing = this ->
|
|
...
|
|
; Thing = that ->
|
|
...
|
|
;
|
|
...
|
|
).
|
|
@end example
|
|
|
|
The Mercury compiler does much better indexing than most Prolog compilers.
|
|
Actually, the compiler indexes on all input variables to a disjunction
|
|
(separate clauses of a predicate are merged into a single clause
|
|
with a disjunction inside the compiler).
|
|
As a consequence, the Mercury compiler indexes on all arguments.
|
|
It also does deep indexing.
|
|
That is, a predicate such as the following will be indexed.
|
|
|
|
@example
|
|
p([f(g(h)) | Rest]) :- ...
|
|
p([f(g(i)) | Rest]) :- ...
|
|
@end example
|
|
|
|
Since indexing is done on disjunctions rather than clauses,
|
|
it is often unnecessary to introduce auxiliary predicates in Mercury,
|
|
whereas in Prolog it is often important to do so for efficiency.
|
|
|
|
If you have a predicate that needs to test all the functors of a type,
|
|
it is better to use a disjunction instead of a chain of conditionals,
|
|
for two reasons.
|
|
First, if you add a new functor to a type,
|
|
the compiler will still accept the now incomplete conditionals,
|
|
whereas if you use a disjunction you will get a determinism error
|
|
that pinpoints which part of the code needs changing.
|
|
Second, in some situations the code generator
|
|
can implement an indexed disjunction (which we call a @emph{switch})
|
|
using a jump table or a hash table,
|
|
which is faster than a chain of if-then-elses.
|
|
|
|
@node Accumulators
|
|
@chapter Accumulators and Difference lists
|
|
|
|
Mercury does not in general allow the kind of aliasing that is used
|
|
in difference lists. Prolog programs using difference lists fall
|
|
in to two categories --- programs whose data flow is ``left-to-right'',
|
|
or can be made left-to-right by reordering conjunctions (the
|
|
Mercury compiler automatically reorders conjunctions so that all
|
|
consumers of a variable come after the producer), and
|
|
those that contain circular dataflow.
|
|
|
|
Programs which do not contain circular dataflow do not cause any trouble
|
|
in Mercury, although the implicit reordering can sometimes mean that programs
|
|
which are tail recursive in Prolog are not tail recursive in Mercury.
|
|
For example, here is a difference-list implementation of quick-sort in Prolog:
|
|
|
|
@example
|
|
qsort(L0, L) :- qsort_2(L0, L - []).
|
|
|
|
qsort_2([], R - R).
|
|
qsort_2([X|L], R0 - R) :-
|
|
partition(L, X, L1, L2),
|
|
qsort_2(L1, R0 - R1),
|
|
R1 = [X|R2],
|
|
qsort_2(L2, R2 - R).
|
|
@end example
|
|
|
|
Due to an unfortunate limitation of the current Mercury implementation
|
|
(partially instantiated modes don't yet work correctly),
|
|
you need to replace all the @samp{-} symbols with commas.
|
|
However, once this is done, and once you have added the appropriate
|
|
declarations, Mercury has no trouble with this code. Although
|
|
the Prolog code is written in a way that traverses the input list left-to-right,
|
|
appending elements to the tail of a difference list to produce the
|
|
output, Mercury will in fact reorder the code so that it traverses
|
|
the input list right-to-left and constructs the output list bottom-up
|
|
rather than top-down. In this particular case, the reordered code is still
|
|
tail recursive - but it is tail-recursive on the first recursive call,
|
|
not the second one!
|
|
|
|
If the occasional loss of tail recursion causes efficiency problems,
|
|
or if the program contains circular data flow, then a different
|
|
solution must be adopted. One way to translate such programs
|
|
is to transform the difference list into an accumulator.
|
|
Instead of appending elements to the end of a difference list by
|
|
binding the tail pointer, you simply insert elements onto the
|
|
front of a list accumulator. At the end of the loop, you can
|
|
call @samp{list__reverse} to put the elements in the correct order
|
|
if necessary. Although this may require two traversals of the list,
|
|
it is still linear in complexity, and it probably still runs faster
|
|
than the Prolog code using difference lists.
|
|
|
|
In most circumstances, the need for difference lists is negated by
|
|
the simple fact that Mercury is efficient enough for them to be
|
|
unnecessary. Occasionally they can lead to a significant improvement
|
|
in the complexity of an operation (mixed insertions and deletions
|
|
from a long queue, for example) and in these situations an alternative
|
|
solution should
|
|
be sought (in the case of queues, the Mercury library uses the
|
|
pair of lists proposed by Richard O'Keefe).
|
|
|
|
@node Determinism
|
|
@chapter Determinism
|
|
|
|
The Mercury language requires that the determinism of all predicates
|
|
exported by a module be declared. The determinism of predicates that
|
|
are local to a module may either be declared or inferred. By default,
|
|
the compiler issues a warning message where such declarations are
|
|
omitted, but this warning can be disabled using the
|
|
@samp{--no-warn-missing-det-decls} option if you want to use
|
|
determinism inference.
|
|
|
|
Determinism checking and inference is an undecidable problem in the
|
|
general case, so it is possible
|
|
to write programs that are deterministic, and have the compiler
|
|
fail to prove the fact. The most important aspect of this problem
|
|
is that the Mercury compiler only detects the clauses of a predicate
|
|
(or the arms of a disjunction, in the general case) to be mutually
|
|
exclusive (and hence deterministic) if they are distinguished by the
|
|
unification of a variable (possibly renamed) with distinct functors
|
|
in the different clauses (or disjuncts), so long as the unifications
|
|
take place before the first call in the clause (or disjunct).
|
|
In these cases, the Mercury compiler generates a @emph{switch} (see
|
|
the earlier section on indexing).
|
|
If a switch has a branch for every functor on the type of the switching
|
|
variable, then the switch cannot fail (though one or more of its arms
|
|
may do so).
|
|
|
|
The Mercury compiler does not do any range checking of integers, so
|
|
code such as:
|
|
|
|
@example
|
|
factorial(0, 1).
|
|
factorial(N, F) :-
|
|
N > 0,
|
|
N1 is N - 1,
|
|
factorial(N1, F1),
|
|
F is F1 * N.
|
|
@end example
|
|
|
|
@noindent
|
|
would be inferred ``nondeterministic''. The compiler would infer that
|
|
the two clauses are not mutually exclusive because it does not know
|
|
about the semantics of @samp{>/2}, and it would infer that
|
|
the predicate as a whole could fail because the call to @samp{>/2}
|
|
can fail.
|
|
|
|
The general solution to such problems is to use an if-then-else:
|
|
|
|
@example
|
|
:- pred factorial(int, int).
|
|
:- mode factorial(in, out) is det.
|
|
|
|
factorial(N, F) :-
|
|
( N =< 0 ->
|
|
F = 1
|
|
;
|
|
N1 is N - 1,
|
|
factorial(N1, F1),
|
|
F is F1 * N
|
|
).
|
|
@end example
|
|
|
|
@node All-solutions
|
|
@chapter All-solutions predicates.
|
|
|
|
Prolog's various different all-solutions predicates (findall/3, bagof/3,
|
|
and setof/3) all have semantic problems.
|
|
Mercury's has a different set of all-solutions predicates (solutions/2,
|
|
solutions_set/2, and unsorted_solutions/2 -- all defined in the library
|
|
module @samp{std_util}) that
|
|
address the problems of the Prolog versions.
|
|
To avoid the variable scoping problems of the Prolog
|
|
versions, rather than taking both a goal to execute and an aliased
|
|
term holding the resulting value to collect, Mercury's all-solutions
|
|
predicates take
|
|
as input a single higher-order predicate term. The Mercury equivalent to
|
|
|
|
@example
|
|
intersect(List1, List2, Intersection) :-
|
|
setof(X, (member(X, List1), member(X, List2)), Intersection).
|
|
@end example
|
|
|
|
@noindent
|
|
is
|
|
|
|
@example
|
|
intersect(List1, List2, Intersection) :-
|
|
solutions(lambda([X::out] is nondet,
|
|
(list__member(X, List1), list__member(X, List2))), Intersection).
|
|
@end example
|
|
|
|
Alternately, this could also be written as
|
|
|
|
@example
|
|
intersect(List1, List2, Intersection) :-
|
|
solutions(member_of_both(List1, List2), Intersection).
|
|
|
|
:- pred member_of_both(list(T)::in, list(T)::in, T::out) is nondet.
|
|
member_of_both(List1, List2, X) :-
|
|
list__member(X, List1), list__member(X, List2).
|
|
@end example
|
|
|
|
@noindent
|
|
and in fact that's exactly how the Mercury compiler implements lambda
|
|
expressions.
|
|
|
|
The current implementation of solutions/2 is a ``zero-copy'' implementation,
|
|
so the cost of solutions/2 is proportional the number of solutions, but
|
|
independent of the size of the solutions. (This may change in
|
|
future implementations.)
|
|
|
|
@bye
|