Files
mercury/doc/transition_guide.texi
Simon Taylor 2725b1a331 Aditi update syntax, type and mode checking.
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.
1999-07-13 08:55:28 +00:00

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