[m-rev.] for review: support different clauses for different modes
Fergus Henderson
fjh at cs.mu.OZ.AU
Wed May 16 06:17:58 AEST 2001
Estimated hours taken: 8
Branches: main
Add support for using different clauses for different modes of a
predicate or function, using mode annotations on the clauses.
compiler/make_hlds.m:
Add support for mode annotations on clauses.
compiler/module_qual.m:
Add new routine qualify_clause_mode_list, for use by make_hlds.m.
compiler/mode_errors.m:
Export output_mode_decl, for use by make_hlds.m
compiler/purity.m:
Treat procedures with different clauses for different modes
as impure, unless promised pure.
compiler/notes/compiler_design.html:
Specify when module qualification of modes in
clause mode annotations is done (in make_hlds.m).
NEWS:
doc/reference_manual.texi:
Document the new feature.
Workspace: /home/mars/fjh/ws1/mercury
Index: NEWS
===================================================================
RCS file: /home/mercury1/repository/mercury/NEWS,v
retrieving revision 1.207
diff -u -d -r1.207 NEWS
--- NEWS 2001/05/02 17:34:26 1.207
+++ NEWS 2001/05/15 18:33:17
@@ -7,6 +7,21 @@
of our CVS repository (the `version-0_10_y' branch).
Changes to the Mercury language:
+* We've extended the language to allow you to specify different clauses
+ for different modes of a predicate or function. This is done by
+ putting a determinism annotation in the head of each clause.
+ For example, you can write
+
+ :- mode p(in).
+ :- mode p(out).
+ p(X::in) :- ... /* clause for the `in' mode */
+ p(X::out) :- ... /* clause for the `out' mode */
+
+ For predicates or functions which have different clauses for different
+ modes, you need to either (1) add a `pragma promise_pure' declaration
+ for the predicate or function, and ensure that the declarative semantics
+ remains the same in each mode, or (2) declare the predicate as impure.
+
* We now allow `:- pragma promise_semipure' declarations. For more
information, see the "Impurity" chapter of the Mercury Language
Reference Manual.
Index: compiler/make_hlds.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/make_hlds.m,v
retrieving revision 1.370
diff -u -d -r1.370 make_hlds.m
--- compiler/make_hlds.m 2001/05/02 17:34:35 1.370
+++ compiler/make_hlds.m 2001/05/15 16:11:07
@@ -94,7 +94,7 @@
:- import_module prog_io, prog_io_goal, prog_io_dcg, prog_io_util, prog_out.
:- import_module modules, module_qual, prog_util, options, hlds_out, typecheck.
:- import_module make_tags, quantification, (inst), globals.
-:- import_module code_util, unify_proc, type_util, mode_util.
+:- import_module code_util, unify_proc, type_util, mode_util, mode_errors.
:- import_module mercury_to_mercury, passes_aux, clause_to_proc, inst_match.
:- import_module fact_table, purity, goal_util, term_util, export, llds.
:- import_module error_util, foreign.
@@ -3716,12 +3716,14 @@
{ pred_info_clauses_info(PredInfo1, Clauses0) },
{ pred_info_typevarset(PredInfo1, TVarSet0) },
{ maybe_add_default_func_mode(PredInfo1, PredInfo2, _) },
- { pred_info_all_procids(PredInfo2, ProcIds) },
- clauses_info_add_clause(Clauses0, ProcIds,
- ClauseVarSet, TVarSet0, Args, Body, Context,
+ select_applicable_modes(Args, ClauseVarSet, Context,
+ PredId, PredInfo2, ModuleInfo1, Info0,
+ ArgTerms, ProcIdsForThisClause, ModuleInfo2, Info1),
+ clauses_info_add_clause(Clauses0, ProcIdsForThisClause,
+ ClauseVarSet, TVarSet0, ArgTerms, Body, Context,
Status, PredOrFunc, Arity, IsAssertion, Goal,
VarSet, TVarSet, Clauses, Warnings,
- ModuleInfo1, ModuleInfo2, Info0, Info),
+ ModuleInfo2, ModuleInfo3, Info1, Info),
{
pred_info_set_clauses_info(PredInfo2, Clauses, PredInfo3),
(
@@ -3741,6 +3743,7 @@
% check if there are still no modes for the predicate,
% and if so, set the `infer_modes' flag for that predicate
%
+ pred_info_all_procids(PredInfo6, ProcIds),
( ProcIds = [] ->
pred_info_get_markers(PredInfo6, Markers0),
add_marker(Markers0, infer_modes, Markers),
@@ -3751,7 +3754,7 @@
map__det_update(Preds0, PredId, PredInfo, Preds),
predicate_table_set_preds(PredicateTable2, Preds,
PredicateTable),
- module_info_set_predicate_table(ModuleInfo2, PredicateTable,
+ module_info_set_predicate_table(ModuleInfo3, PredicateTable,
ModuleInfo)
},
( { Status \= opt_imported } ->
@@ -3766,6 +3769,130 @@
)
).
+ % Extract the mode annotations (if any) from the clause arguments,
+ % and determine which mode(s) this clause should apply to.
+
+:- pred select_applicable_modes(list(prog_term)::in, prog_varset::in,
+ prog_context::in, pred_id::in, pred_info::in,
+ module_info::in, qual_info::in,
+ list(prog_term)::out, list(proc_id)::out,
+ module_info::out, qual_info::out,
+ io__state::di, io__state::uo) is det.
+
+select_applicable_modes(Args0, VarSet, Context, PredId, PredInfo, ModuleInfo0,
+ Info0, Args, ProcIds, ModuleInfo, Info) -->
+ { get_mode_annotations(Args0, Args, empty, ModeAnnotations) },
+ (
+ { ModeAnnotations = modes(ModeList0) },
+
+ %
+ % The user specified some mode annotations on this clause.
+ % First module-qualify the mode annotations.
+ %
+ { qual_info_get_mq_info(Info0, MQInfo0) },
+ module_qual__qualify_clause_mode_list(ModeList0, ModeList,
+ Context, MQInfo0, MQInfo),
+ { qual_info_set_mq_info(Info0, MQInfo, Info) },
+
+ %
+ % Now find the procedure which matches these mode annotations.
+ %
+ { pred_info_procedures(PredInfo, Procs) },
+ { map__to_assoc_list(Procs, ExistingProcs) },
+ (
+ { get_procedure_matching_declmodes(ExistingProcs,
+ ModeList, ModuleInfo0, ProcId) }
+ ->
+ { ProcIds = [ProcId] },
+ { ModuleInfo = ModuleInfo0 }
+ ;
+ { module_info_incr_errors(ModuleInfo0, ModuleInfo) },
+ undeclared_mode_error(
+ ModeList, VarSet, PredId, PredInfo,
+ ModuleInfo, Context),
+ % apply the clause to all modes
+ % XXX would it be better to apply it to none?
+ { pred_info_all_procids(PredInfo, ProcIds) }
+ )
+ ;
+ { ModeAnnotations = empty },
+ { pred_info_all_procids(PredInfo, ProcIds) },
+ { ModuleInfo = ModuleInfo0 },
+ { Info = Info0 }
+ ;
+ { ModeAnnotations = none },
+ { pred_info_all_procids(PredInfo, ProcIds) },
+ { ModuleInfo = ModuleInfo0 },
+ { Info = Info0 }
+ ;
+ { ModeAnnotations = mixed },
+ { module_info_incr_errors(ModuleInfo0, ModuleInfo) },
+ { Info = Info0 },
+ io__set_exit_status(1),
+ prog_out__write_context(Context),
+ io__write_string("In clause for "),
+ hlds_out__write_pred_id(ModuleInfo, PredId),
+ io__write_string(":\n"),
+ prog_out__write_context(Context),
+ io__write_string(
+ " syntax error: some but not all arguments have mode annotations.\n"),
+ % apply the clause to all modes
+ % XXX would it be better to apply it to none?
+ { pred_info_all_procids(PredInfo, ProcIds) }
+ ).
+
+ % Clauses can have mode annotations on them, to indicate that the
+ % clause should only be used for particular modes of a predicate.
+ % This type specifies the mode annotations on a clause.
+:- type mode_annotations
+ ---> empty % No arguments.
+ ; none % One or more arguments,
+ % each without any mode annotations.
+ ; modes(list(mode))
+ % One or more arguments, each with a mode annotation.
+ ; mixed % Two or more arguments, including some with mode
+ % annotations and some without. (This is not allowed.)
+ .
+
+
+ % Extract the mode annotations (if any) from a list of arguments.
+:- pred get_mode_annotations(list(prog_term)::in, list(prog_term)::out,
+ mode_annotations::in, mode_annotations::out) is det.
+
+get_mode_annotations([], [], Annotations, Annotations).
+get_mode_annotations([Arg0 | Args0], [Arg | Args],
+ Annotations0, Annotations) :-
+ get_mode_annotation(Arg0, Arg, MaybeAnnotation),
+ add_annotation(Annotations0, MaybeAnnotation, Annotations1),
+ get_mode_annotations(Args0, Args, Annotations1, Annotations).
+
+:- pred add_annotation(mode_annotations::in, maybe(mode)::in,
+ mode_annotations::out) is det.
+
+add_annotation(empty, no, none).
+add_annotation(empty, yes(Mode), modes([Mode])).
+add_annotation(modes(_), no, mixed).
+add_annotation(modes(Modes), yes(Mode), modes(Modes ++ [Mode])).
+add_annotation(none, no, none).
+add_annotation(none, yes(_), mixed).
+add_annotation(mixed, _, mixed).
+
+ % Extract the mode annotations (if any) from a single argument.
+:- pred get_mode_annotation(prog_term::in, prog_term::out, maybe(mode)::out)
+ is det.
+
+get_mode_annotation(Arg0, Arg, MaybeAnnotation) :-
+ (
+ Arg0 = term__functor(term__atom("::"), [Arg1, ModeTerm], _),
+ convert_mode(term__coerce(ModeTerm), Mode)
+ ->
+ Arg = Arg1,
+ MaybeAnnotation = yes(Mode)
+ ;
+ Arg = Arg0,
+ MaybeAnnotation = no
+ ).
+
%-----------------------------------------------------------------------------%
%
% Generate the clauses_info for the introduced predicate that we generate
@@ -7603,6 +7730,9 @@
prog_out__write_sym_name_and_arity(qualified(Module,Name)/Arity),
io__write_string("'.\n").
+ % Similar to undeclared_mode_error, but gives less information.
+ % XXX perhaps we should get rid of this, and change the callers to
+ % instead call undeclared_mode_error.
:- pred undefined_mode_error(sym_name, int, prog_context, string,
io__state, io__state).
:- mode undefined_mode_error(in, in, in, in, di, uo) is det.
@@ -7617,6 +7747,60 @@
io__write_string(" `"),
prog_out__write_sym_name_and_arity(Name/Arity),
io__write_string("' specifies non-existent mode.\n").
+
+ % Similar to undefined_mode_error, but gives more information.
+:- pred undeclared_mode_error(list(mode)::in, prog_varset::in,
+ pred_id::in, pred_info::in, module_info::in, prog_context::in,
+ io__state::di, io__state::uo) is det.
+
+undeclared_mode_error(ModeList, VarSet, PredId, PredInfo, ModuleInfo,
+ Context) -->
+ prog_out__write_context(Context),
+ io__write_string("In clause for "),
+ hlds_out__write_pred_id(ModuleInfo, PredId),
+ io__write_string(":\n"),
+ prog_out__write_context(Context),
+ io__write_string(
+ " error: mode annotation specifies undeclared mode\n"),
+ prog_out__write_context(Context),
+ io__write_string(" `"),
+ { strip_builtin_qualifiers_from_mode_list(ModeList,
+ StrippedModeList) },
+ { pred_info_get_is_pred_or_func(PredInfo, PredOrFunc) },
+ { pred_info_name(PredInfo, Name) },
+ { MaybeDet = no },
+ mercury_output_mode_subdecl(PredOrFunc,
+ varset__coerce(VarSet),
+ unqualified(Name), StrippedModeList,
+ MaybeDet, Context),
+ io__write_string("'\n"),
+ prog_out__write_context(Context),
+ io__write_string(" of "),
+ hlds_out__write_pred_id(ModuleInfo, PredId),
+ io__write_string(".\n"),
+ globals__io_lookup_bool_option(verbose_errors,
+ VerboseErrors),
+ { pred_info_all_procids(PredInfo, ProcIds) },
+ ( { ProcIds = [] } ->
+ prog_out__write_context(Context),
+ io__write_string(
+ " (There are no declared modes for this "),
+ hlds_out__write_pred_or_func(PredOrFunc),
+ io__write_string(".)\n")
+ ; { VerboseErrors = yes } ->
+ io__write_string(
+ "\tThe declared modes for this "),
+ hlds_out__write_pred_or_func(PredOrFunc),
+ io__write_string(" are the following:\n"),
+ { OutputProc =
+ (pred(ProcId::in, di, uo) is det -->
+ io__write_string("\t\t:- mode "),
+ output_mode_decl(ProcId, PredInfo),
+ io__write_string(".\n")) },
+ list__foldl(OutputProc, ProcIds)
+ ;
+ []
+ ).
:- pred maybe_undefined_pred_error(sym_name, int, pred_or_func, import_status,
bool, prog_context, string, io__state, io__state).
Index: compiler/mode_errors.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/mode_errors.m,v
retrieving revision 1.70
diff -u -d -r1.70 mode_errors.m
--- compiler/mode_errors.m 2001/02/28 12:46:24 1.70
+++ compiler/mode_errors.m 2001/05/15 15:18:02
@@ -178,6 +178,9 @@
pred_id, pred_info, module_info, io__state, io__state).
:- mode report_indistinguishable_modes_error(in, in, in, in, in, di, uo) is det.
+:- pred output_mode_decl(proc_id, pred_info, io__state, io__state).
+:- mode output_mode_decl(in, in, di, uo) is det.
+
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
@@ -1187,9 +1190,6 @@
prog_out__write_context(OldContext),
io__write_string(
" Here is the conflicting mode declaration.\n").
-
-:- pred output_mode_decl(proc_id, pred_info, io__state, io__state).
-:- mode output_mode_decl(in, in, di, uo) is det.
output_mode_decl(ProcId, PredInfo) -->
{ pred_info_get_is_pred_or_func(PredInfo, PredOrFunc) },
Index: compiler/module_qual.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/module_qual.m,v
retrieving revision 1.66
diff -u -d -r1.66 module_qual.m
--- compiler/module_qual.m 2001/05/02 17:34:39 1.66
+++ compiler/module_qual.m 2001/05/15 15:52:58
@@ -44,6 +44,14 @@
:- mode module_qual__qualify_lambda_mode_list(in, out,
in, in, out, di, uo) is det.
+ % This is called from make_hlds.m to qualify the modes in a
+ % clause mode annotation.
+:- pred module_qual__qualify_clause_mode_list(list(mode), list(mode),
+ prog_context, mq_info, mq_info,
+ io__state, io__state) is det.
+:- mode module_qual__qualify_clause_mode_list(in, out,
+ in, in, out, di, uo) is det.
+
% This is called from make_hlds.m to qualify an
% explicit type qualification.
:- pred module_qual__qualify_type_qualification(type, type, prog_context,
@@ -126,6 +134,11 @@
{ mq_info_set_error_context(Info0, lambda_expr - Context, Info1) },
qualify_mode_list(Modes0, Modes, Info1, Info).
+module_qual__qualify_clause_mode_list(Modes0, Modes, Context, Info0, Info) -->
+ { mq_info_set_error_context(Info0, clause_mode_annotation - Context,
+ Info1) },
+ qualify_mode_list(Modes0, Modes, Info1, Info).
+
module_qual__qualify_type_qualification(Type0, Type, Context, Info0, Info) -->
{ mq_info_set_error_context(Info0, type_qual - Context, Info1) },
qualify_type(Type0, Type, Info1, Info).
@@ -1213,6 +1226,7 @@
; func_mode(id)
; (pragma)
; lambda_expr
+ ; clause_mode_annotation
; type_qual
; class(id)
; instance(id).
@@ -1313,6 +1327,8 @@
write_id(Id).
write_error_context2(lambda_expr) -->
io__write_string("mode declaration for lambda expression").
+write_error_context2(clause_mode_annotation) -->
+ io__write_string("clause mode annotation").
write_error_context2(pragma) -->
io__write_string("pragma").
write_error_context2(type_qual) -->
Index: compiler/purity.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/purity.m,v
retrieving revision 1.31
diff -u -d -r1.31 purity.m
--- compiler/purity.m 2001/04/07 14:04:56 1.31
+++ compiler/purity.m 2001/05/15 16:39:09
@@ -14,6 +14,8 @@
% The main purpose of this module is check the consistency of the
% `impure' and `promise_pure' (etc.) declarations, and to thus report
% error messages if the program is not "purity-correct".
+% This includes treating procedures with different clauses for
+% different modes as impure, unless promised pure.
%
% This module also does two final parts of type analysis:
% - it resolves predicate overloading
@@ -429,13 +431,14 @@
{ NumErrors0 = 0 }
;
{ pred_info_clauses_info(PredInfo0, ClausesInfo0) },
+ { pred_info_procids(PredInfo0, ProcIds) },
{ clauses_info_clauses(ClausesInfo0, Clauses0) },
{ clauses_info_vartypes(ClausesInfo0, VarTypes0) },
{ clauses_info_varset(ClausesInfo0, VarSet0) },
{ RunPostTypecheck = yes },
{ PurityInfo0 = purity_info(ModuleInfo, RunPostTypecheck,
PredInfo0, VarTypes0, VarSet0, []) },
- { compute_purity(Clauses0, Clauses, pure, Purity,
+ { compute_purity(Clauses0, Clauses, ProcIds, pure, Purity,
PurityInfo0, PurityInfo) },
{ PurityInfo = purity_info(_, _, PredInfo1,
VarTypes, VarSet, RevMessages) },
@@ -552,18 +555,45 @@
% Infer the purity of a single (non-pragma c_code) predicate
-:- pred compute_purity(list(clause), list(clause),
+:- pred compute_purity(list(clause), list(clause), list(proc_id),
purity, purity, purity_info, purity_info).
-:- mode compute_purity(in, out, in, out, in, out) is det.
+:- mode compute_purity(in, out, in, in, out, in, out) is det.
-compute_purity([], [], Purity, Purity) --> [].
-compute_purity([Clause0|Clauses0], [Clause|Clauses], Purity0, Purity) -->
+compute_purity([], [], _, Purity, Purity) --> [].
+compute_purity([Clause0|Clauses0], [Clause|Clauses], ProcIds,
+ Purity0, Purity) -->
{ Clause0 = clause(Ids, Body0 - Info0, Context) },
- compute_expr_purity(Body0, Body, Info0, no, Bodypurity),
+ compute_expr_purity(Body0, Body, Info0, no, Bodypurity0),
+ % If this clause doesn't apply to all modes of this procedure,
+ % i.e. the procedure has different clauses for different modes,
+ % then we must treat it as impure.
+ {
+ applies_to_all_modes(Clause0, ProcIds)
+ ->
+ Clausepurity = (pure)
+ ;
+ Clausepurity = (impure)
+ },
+ { worst_purity(Bodypurity0, Clausepurity, Bodypurity) },
{ add_goal_info_purity_feature(Info0, Bodypurity, Info) },
{ worst_purity(Purity0, Bodypurity, Purity1) },
{ Clause = clause(Ids, Body - Info, Context) },
- compute_purity(Clauses0, Clauses, Purity1, Purity).
+ compute_purity(Clauses0, Clauses, ProcIds, Purity1, Purity).
+
+:- pred applies_to_all_modes(clause::in, list(proc_id)::in) is semidet.
+
+applies_to_all_modes(clause(ClauseProcIds, _, _), ProcIds) :-
+ (
+ % an empty list here means that the clause applies
+ % to *all* procedures
+ ClauseProcIds = []
+ ;
+ % Otherwise the clause applies to the procids in the
+ % list. Check if this is the same as the procids for
+ % this procedure.
+ list__sort(ClauseProcIds, SortedIds),
+ SortedIds = ProcIds
+ ).
:- pred compute_expr_purity(hlds_goal_expr, hlds_goal_expr,
hlds_goal_info, bool, purity, purity_info, purity_info).
Index: compiler/notes/compiler_design.html
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/notes/compiler_design.html,v
retrieving revision 1.60
diff -u -d -r1.60 compiler_design.html
--- compiler/notes/compiler_design.html 2001/05/15 07:12:01 1.60
+++ compiler/notes/compiler_design.html 2001/05/15 15:48:39
@@ -184,9 +184,9 @@
<li> all types, typeclasses, insts and modes occuring in pred, func,
type, typeclass and mode declarations are module qualified by
module_qual.m.
- <li> all types, insts and modes occuring in lambda expressions and
- explicit type qualifications are module qualified in
- make_hlds.m.
+ <li> all types, insts and modes occuring in lambda expressions,
+ explicit type qualifications, and clause mode annotations
+ are module qualified in make_hlds.m.
<li> constructors occuring in predicate and function mode declarations
are module qualified during type checking.
<li> predicate and function calls and constructors within goals
Index: doc/reference_manual.texi
===================================================================
RCS file: /home/mercury1/repository/mercury/doc/reference_manual.texi,v
retrieving revision 1.205
diff -u -d -r1.205 reference_manual.texi
--- doc/reference_manual.texi 2001/04/29 07:54:29 1.205
+++ doc/reference_manual.texi 2001/05/15 20:12:18
@@ -534,7 +534,10 @@
determines which predicate or function the fact belongs to;
the predicate or function must have been declared
in a preceding @samp{pred} or @samp{func} declaration in this module.
-The arguments of the head must be valid data-terms.
+The @var{Result} (if any) and the arguments of the @var{Head} must
+be valid data-terms (optionally annotated with a mode qualifier;
+see @pxref{Different clauses for different modes}).
+
A fact is equivalent to a rule whose body is @samp{true}.
@node Rules
@@ -550,7 +553,9 @@
function the clause belongs to; the predicate or function must have
been declared in a preceding @samp{pred} or @samp{func} declaration in
this module.
-The arguments of the head must be valid data-terms.
+The @var{Result} and the arguments of the @var{Head} must be
+valid data-terms (optionally annotated with a mode qualifier;
+see @pxref{Different clauses for different modes}).
The @var{Body} must be a valid goal.
@node Goals
@@ -1732,6 +1737,15 @@
@node Modes
@chapter Modes
+ at menu
+* Insts modes and mode definitions::
+* Predicate and function mode declarations::
+* Different clauses for different modes::
+ at end menu
+
+ at node Insts modes and mode definitions
+ at section Insts, modes, and mode definitions
+
The @dfn{mode} of a predicate, or function, is a mapping
from the initial state of instantiation of the arguments of the predicate,
or the arguments and result of a function,
@@ -1884,6 +1898,9 @@
and arity in the same module. Similarly, there must not be more
than one mode definition with the same name and arity in the same module.
+ at node Predicate and function mode declarations
+ at section Predicate and function mode declarations
+
A @dfn{predicate mode declaration}
assigns a mode mapping to each argument of a predicate.
A @dfn{function mode declaration}
@@ -2009,8 +2026,8 @@
with respect to a given mode declaration}
if given that the predicates and functions called by @var{p}
all satisfy their mode declaration constraints,
-there exists an ordering of the literals in the definition of @var{p}
-such that
+there exists an ordering of the conjuncts in each conjunction
+in the clauses of @var{p} such that
@itemize @bullet
@item
@@ -2040,6 +2057,116 @@
The mode analysis algorithm annotates each call with the mode used.
+ at node Different clauses for different modes
+ at section Different clauses for different modes
+
+Because the compiler automatically reorders conjunctions to
+satisfy the modes, it is often possible for a single clause
+to satisfy different modes. However, occaisionally reordering
+of conjunctions is not sufficient; you may want to write different
+code for different modes.
+
+For example, the usual code for list append
+
+ at example
+ append([], Ys, Ys).
+ append([X|Xs], Ys, [X|Zs]) :- append(Xs, Ys, Zs).
+ at end example
+
+ at noindent
+works fine in most modes, but is not very satisfactory for the
+ at samp{append(out, in, in)} mode of append, because although
+every call in this mode only has at most one solution,
+the compiler's determinism inference will not be able to
+infer that. This means that using the usual code for append in
+this mode will be inefficient, and the overly conservative determinism
+inference may cause spurious determinism errors later.
+
+For this mode, it is better to use a completely different algorithm:
+
+ at example
+ append(Prefix, Suffix, List) :-
+ list__length(List, ListLength),
+ list__length(Suffix, SuffixLength),
+ PrefixLength is ListLength - SuffixLength,
+ list__split_list(PrefixLength, List, Prefix, Suffix).
+ at end example
+
+ at noindent
+However, that code doesn't work in the other modes of append.
+
+To handle such cases, you can use mode annotations on clauses, which
+indicate that particular clauses should only be used for particular modes.
+To specify that a clause only applies to a given mode, each argument
+ at var{Arg} of the clause head should be annotated with the corresponding
+argument mode @var{Mode}, using the @samp{::} mode qualification operator,
+i.e. @samp{@var{Arg} :: @var{Mode}}.
+
+For example, if append was declared as
+
+ at example
+ :- pred append(list(T), list(T), list(T)).
+ :- mode append(in, in, out).
+ :- mode append(out, out, in).
+ :- mode append(in, out, in).
+ :- mode append(out, in, in).
+ at end example
+
+ at noindent
+then you could implement it as
+
+ at example
+ append(L1::in, L2::in, L3::out) :- usual_append(L1, L2, L3).
+ append(L1::out, L2::out, L3::in) :- usual_append(L1, L2, L3).
+ append(L1::in, L2::out, L3::in) :- usual_append(L1, L2, L3).
+ append(L1::out, L2::in, L3::in) :- other_append(L1, L2, L3).
+
+ usual_append([], Ys, Ys).
+ usual_append([X|Xs], Ys, [X|Zs]) :- usual_append(Xs, Ys, Zs).
+
+ other_append(Prefix, Suffix, List) :-
+ list__length(List, ListLength),
+ list__length(Suffix, SuffixLength),
+ PrefixLength is ListLength - SuffixLength,
+ list__split_list(PrefixLength, List, Prefix, Suffix).
+ at end example
+
+This language feature can be used to write ``impure'' code that
+doesn't have any consistent declarative semantics. For example,
+you can easily use it to write something similar to Prolog's (in)famous
+var/1 predicate:
+
+ at example
+ :- mode var(in).
+ :- mode var(free>>free).
+ var(_::in) :- fail.
+ var(_::free>>free) :- true.
+ at end example
+
+ at noindent
+As you can see, in this case the two clauses are @emph{not} equivalent.
+
+Because of this possibility, predicates or functions which are defined
+using different code for different modes are by default assumed to be
+impure; the programmer must either (1) carefully ensure that the
+logical meaning of the clauses is the same for all modes,
+in which case a @samp{pragma promise_pure} declaration can be used
+or (2) declare the predicate or function as impure.
+ at xref{Impurity}.
+
+In the example with @samp{append} above, the two ways of implementing
+append do have the same declarative semantics, so we can safely use
+the first approach:
+
+ at example
+ :- pragma promise_pure(append/3).
+ at end example
+
+In the example with @samp{var/1} above, the two clauses have different
+semantics, so the predicate must be declared as impure:
+
+ :- impure pred var(T).
+
@node Unique modes
@chapter Unique modes
@@ -5068,7 +5195,7 @@
For each Mercury module containing @samp{pragma export} declarations,
the Mercury implementation will automatically create a header file
-for that module which declares a C function @var{C_Name()}
+for that module which declares a C function @var{C_Name}()
for each of the @samp{pragma export} declarations.
Each such C function is the C interface to the specified mode of
the specified Mercury predicate or function.
--
Fergus Henderson <fjh at cs.mu.oz.au> | "I have always known that the pursuit
| of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.
--------------------------------------------------------------------------
mercury-reviews mailing list
post: mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------
More information about the reviews
mailing list