[m-dev.] for review: Aditi updates [1]

Simon Taylor stayl at cs.mu.OZ.AU
Wed Apr 12 18:25:35 AEST 2000


Hi,

I'm mainly interested in having the changes to the documentation
reviewed, but if anyone wants to review the code changes that would
be good.

Unless there are any strong objections, I'll commit this on Friday
afternoon whether it has been reviewed or not to avoid having to wait
a week for it to be installed on taifun.

Thanks,
Simon.



Estimated hours taken: 120

Finish the implementation of Aditi updates.

compiler/hlds_goal.m:
	Refactor the aditi_builtin type so that operations with
	similar syntax and implementations are grouped together.

	Add operations to delete a single tuple (`aditi_delete') and 
	to modify tuples (`aditi_bulk_modify').

compiler/*.m:
	Minor changes required by refactoring the aditi_builtin type.

compiler/make_hlds.m:
	Parse `aditi_delete' and `aditi_bulk_modify' goals.

	Parse a nicer syntax for `aditi_bulk_delete' and
	`aditi_bulk_insert' goals
	(e.g. aditi_bulk_delete(p(_, X, _) :- X > 2)).

compiler/rl_out.pp:
	For each base relation, generate RL procedures to apply
	deletions and modifications to the relation. `aditi_bulk_modify'
	takes a closure which produces tuples which contain both the
	attributes of the tuple to delete and the tuple to insert. The
	modification RL procedure performs two projections on the
	closure result to produce the tuples to delete and the tuples
	to delete.

	The input stream to the rl_PROC_delete RL instruction must contain
	only tuples taken from the relation to delete from -- the deletion
	is done using the tuple-id rather than the tuple contents.
	The generated deletion procedure performs a semi-join of the
	relation to delete from with the relation containing the
	tuples to delete.

compiler/rl.m:
	Add predicates to generate the names of the modification
	and deletion RL procedures generated for each base relation.

compiler/rl_exprn.m:
	Generate the projection expressions required by the modification
	RL procedures generated for each base relation.

	Generate the equi-join expressions required by the modification
	and deletion RL procedures generated for each base relation.

compiler/unify_gen.m:
	Implement code generation for `aditi_bottom_up' closures.

compiler/rl.m:
compiler/magic.m:
	Factor out the code used to create the name of the RL procedure
	used to interface top-down Mercury to Aditi, for use by unify_gen.m
	to generate `aditi_bottom_up' closures.

compiler/code_util.m:
	Add predicate code_util__make_user_proc_label, which constructs
	a label name from the given all the individual pieces of information
	used to construct it, rather than just a pred_id and proc_id.
	This is used to produce the RL procedure name for an `aditi_bottom_up'
	closure, so that the code doesn't have to work out what the pred_id
	of the magic sets transformed procedure is.

compiler/magic.m:
	Always create new procedures to interface Mercury to Aditi,
	to make it easier for unify_gen.m to work out what the name
	of the interface procedure is -- don't optimize the case of
	a predicate with no input arguments.

	Alter the goal generated for the interface procedures so
	that it doesn't matter whether the interface procedure
	and the procedure it interfaces to are compiled together
	by rl_gen.m -- the old code generated for these procedures
	assumed they were compiled separately, which wasn't always the
	case.

	Don't pass a `magic_info' through the code to generate the
	C interface procedures -- only the module_info field was
	required.

compiler/magic_util.m:
compiler/magic.m:
compiler/context.m:
	Don't pass a `magic_info' through
	magic_util__create_input_test_unifications --
	only the module_info and proc_info fields were used.

compiler/post_typecheck.m:
compiler/magic_util.m:
	Don't report errors for the second `aditi__state' argument
	of the closure passed to `aditi_bulk_modify'. 

compiler/purity.m:
	Change the mode of the second `aditi__state' argument
	of the closure passed to `aditi_bulk_modify' to `unused'.

compiler/call_gen.m:
	Generate `aditi_delete' and `aditi_bulk_modify'.

	Remove the `aditi__state' from the tuple to insert passed
	to an `aditi_insert' or `aditi_delete' operation -- the relation
	on disk does not contain the `aditi__state' attribute.

extras/aditi/aditi.m:
	Implement the updates.

	Allocate all memory on the Mercury heap to avoid memory
	leaks when a transaction aborts.

	Uncaught exceptions within a transaction now cause
	the transaction to abort, and the exception is rethrown
	to the caller.

	If using trailing, add trail entries for the output relation
	and cursor created for a call to an Aditi procedure, so that
	they will be cleaned up if only one solution of the call
	is needed or if an exception is thrown.

	Include line numbers in the debugging messages if an Aditi API
	function fails.

compiler/llds.m:
compiler/*.m:
	Rename the `do_aditi_modify' label to `do_aditi_bulk_modify' -- we
	may eventually want to implement a modification goal type which
	doesn't produce all modified tuples before applying the update.

doc/reference_manual.texi:
	Document `aditi_delete' and `aditi_bulk_modify'.

	Add some extra spacing in the Aditi update section to
	improve readability.

tests/valid/aditi_update.m:
tests/invalid/aditi_update_errors.m:
tests/invalid/aditi_update_errors.err_exp:
tests/invalid/aditi_update_mode_errors.m:
tests/invalid/aditi_update_mode_errors.err_exp:
	Changed to fit in with the new syntax.

tests/valid/Mmakefile:
	Code generation for Aditi updates now works, so enable full
	compilation of the aditi_update.m test case, rather than
	just checking for errors.


Index: doc/reference_manual.texi
===================================================================
RCS file: /home/mercury1/repository/mercury/doc/reference_manual.texi,v
retrieving revision 1.178
diff -u -u -r1.178 reference_manual.texi
--- doc/reference_manual.texi	2000/03/22 14:45:18	1.178
+++ doc/reference_manual.texi	2000/04/12 07:34:03
@@ -557,9 +557,10 @@
 
 @item @code{aditi_bulk_delete(@dots{})}
 @item @code{aditi_bulk_insert(@dots{})}
+ at item @code{aditi_bulk_modify(@dots{})}
 @item @code{aditi_delete(@dots{})}
 @item @code{aditi_insert(@dots{})}
- at item @code{aditi_modify(@dots{})}
+
 These goal forms are used for the Aditi database interface.
 @xref{Aditi update syntax}.
 
@@ -6197,8 +6198,7 @@
 
 @menu 
 * Aditi update notes::
-* Insertion::
-* Deletion::
+* Insertion and deletion::
 * Bulk insertion and deletion::
 * Modification::
 @end menu
@@ -6232,8 +6232,8 @@
 :- pragma aditi(ancestor/3).
 @end example
 
- at node Insertion
- at subsubsection Insertion
+ at node Insertion and deletion
+ at subsubsection Insertion and deletion
 
 @example
 aditi_insert(@var{PredName}(@var{Arg1}, @var{Arg2}, @dots{}), @var{DB0}, @var{DB}).
@@ -6241,8 +6241,24 @@
 aditi_insert(@var{FuncName}(@var{Arg1}, @var{Arg2}, @dots{}) = @var{RetVal}, @var{DB0}, @var{DB}).
 @end example
 
+ at sp 1
+
 Insert the specified tuple into a relation.
 
+ at sp 1
+
+ at example
+aditi_delete(@var{PredName}(@var{Arg1}, @var{Arg2}, @dots{}), @var{DB0}, @var{DB}).
+
+aditi_delete(@var{FuncName}(@var{Arg1}, @var{Arg2}, @dots{}) = @var{RetVal}, @var{DB0}, @var{DB}).
+ at end example
+
+ at sp 1
+
+Delete the specified tuple from a relation.
+
+ at sp 1
+
 @itemize @bullet
 @item
 @samp{@var{PredName}} must be the name of a predicate. 
@@ -6264,11 +6280,16 @@
 @samp{aditi__state}. They have mode @w{@samp{aditi_di, aditi_uo}}.
 @end itemize
 
+ at sp 1
+
 Note that @w{@samp{@var{PredName}(@var{Arg1}, @var{Arg2}, @dots{})}}
-in an @samp{aditi_insert} goal is not a higher-order term.
+in an @samp{aditi_insert} or @samp{aditi_delete} goal is not a
+higher-order term.
 @w{@samp{Pred = p(DB0, X, Y), aditi_insert(Pred, DB0, DB)}}
 is a syntax error.
 
+ at sp 1
+
 Examples:
 @example
 insert_example_1(DB0, DB) :-
@@ -6276,136 +6297,48 @@
 
 insert_example_2(DB0, DB) :-
         aditi_insert(f(_, 1) = 2, DB0, DB).
- at end example
-
- at node Deletion
- at subsubsection Deletion
-
- at example
-aditi_delete((@var{PredName}(@var{Arg1}, @var{Arg2}, @dots{}) :- @var{Goal}), @var{DB0}, @var{DB}).
-
-aditi_delete((@var{FuncName}(@var{Arg1}, @var{Arg2}, @dots{}) = @var{RetVal} :- @var{Goal}), @var{DB0}, @var{DB}).
-
-aditi_delete(@var{PredOrFunc} @var{Name}/@var{Arity}, @var{Closure}, @var{DB0}, @var{DB}).
- at end example
-
-Delete all tuples for which @samp{@var{Goal}} or @samp{@var{Closure}} 
-succeeds from the named base relation. 
-
- at itemize @bullet
- at item
- at samp{@var{PredName}} must be the name of a predicate. 
-
- at item
- at samp{@var{FuncName}} must be the name of a function.
-
- at item
- at samp{@var{PredOrFunc}} must be either @samp{pred} or @samp{func}.
-If it is @samp{pred}, then @samp{@var{Name}} must be the name of
-a predicate, and if it is @samp{func}, then @samp{@var{Name}}
-must be the name of a function.
 
- at item
- at samp{@var{Arity}} must be the arity of the predicate or function
-being updated.
-
- at item
- at samp{@var{Arg1}}, @samp{@var{Arg2}}, @dots{} and @samp{@var{RetVal}}
-must be data-terms. The head of the deletion rule must have the same
-type signature as the relation being deleted from. The arguments
-(including the return value of a function) all have mode @samp{in},
-except for the @samp{aditi__state} argument, which has mode
- at samp{unused} --- it is not possible to call an Aditi relation
-from the deletion goal.
-
- at item
- at samp{@var{Goal}} must be a Mercury goal.
-
- at item
- at samp{@var{Closure}} must be a data-term which has a higher-order type.
-
-When deleting from a predicate with type declaration
- at w{@samp{:- pred p(aditi__state, @var{Type1}, @dots{})}},
- at samp{@var{Closure}} must have type
- at w{@samp{aditi_top_down pred(aditi__state, @var{Type1}, @dots{})}},
-and inst @w{@samp{pred(unused, in, @dots{}) is semidet}}.
-
-When deleting from a function with type declaration
- at w{@samp{:- func p(aditi__state, @var{Type1}, @dots{}) = @var{Type2}}},
- at samp{@var{Closure}} must have type
- at w{@samp{aditi_top_down func(aditi__state, @var{Type1}, @dots{}) = @var{Type2}}},
-and inst @w{@samp{func(unused, in, @dots{}) = in is semidet}}.
-
-The @samp{aditi_top_down} annotation on the lambda expression is needed to 
-tell the compiler to generate code for execution by the
-Aditi database using the normal Mercury execution algorithm.
-
-The @samp{aditi__state} argument of @samp{@var{Closure}} must have
-mode @samp{unused} --- it is not possible to call an Aditi
-relation from the deletion condition. All other arguments of
- at samp{@var{Closure}} must have mode @samp{in}.
-
-If the construction of @samp{@var{Closure}} is in the same conjunction
-as the @samp{aditi_delete} call, the compiler may be able to do a better
-job of optimizing the deletion using indexes.
-
- at item
- at samp{@var{DB0}} and @samp{@var{DB}} must be data-terms of type
- at samp{aditi__state}. They have mode @w{@samp{aditi_di, aditi_uo}}.
- at end itemize
-
-Examples:
- at example
 delete_example_1(DB0, DB) :-
-        aditi_delete((p(_, X, Y) :- X + Y = 2), DB0, DB).
+        aditi_delete(p(_, 1, 2), DB0, DB).
 
 delete_example_2(DB0, DB) :-
-        aditi_delete(f(_, 2) = _Y, DB0, DB).
+        aditi_delete(f(_, 1) = 2, DB0, DB).
 
-delete_example_3(DB0, DB) :-
-        DeleteP = (aditi_top_down
-               pred(_::unused, X::in, Y::in) is semidet :-
-                        X = 2
-               ),
-        aditi_delete(pred p/3, DeleteP, DB0, DB).
-
-delete_example_4(DB0, DB) :-
-        DeleteQ = (aditi_top_down
-               func(_::unused, X::in) = (Y::in) is semidet :-
-                        X = 2
-               ),
-        aditi_delete(func f/2, DeleteQ, DB0, DB).
-
-delete_example_5 -->
-        aditi_delete((p(_, X, Y) :- X = 2, Y = 2)). 
-
 @end example
 
-The type of @samp{DeleteP} is
- at w{@samp{aditi_top_down pred(aditi__state, int, int)}}.
-Its inst is @w{@samp{pred(unused, in, in) is semidet}}, as for a normal
-lambda expression.
-
-Note that in @samp{delete_example_5} the extra set of parentheses around
-the goal are needed, otherwise the second goal in the conjunction
-in the deletion goal would be parsed as an extra argument of the
- at samp{aditi_delete} call, resulting in a syntax error.
-
 @node Bulk insertion and deletion
 @subsubsection Bulk insertion and deletion
 
 @example
+aditi_bulk_insert((@var{PredName}(@var{Arg1}, @var{Arg2}, @dots{}) :- @var{Goal}), @var{DB0}, @var{DB}).
+
+aditi_bulk_insert((@var{FuncName}(@var{Arg1}, @var{Arg2}, @dots{}) = @var{RetVal} :- @var{Goal}), @var{DB0}, @var{DB}).
+
 aditi_bulk_insert(@var{PredOrFunc} @var{Name}/@var{Arity}, @var{Closure}, @var{DB0}, @var{DB}).
 @end example
 
-Insert all solutions of @samp{@var{Closure}} into the named relation.
+ at sp 1
 
+Insert all solutions of @samp{@var{Goal}} or @samp{@var{Closure}} into
+the named relation.
+
+ at sp 1
+
 @example
+aditi_bulk_delete((@var{PredName}(@var{Arg1}, @var{Arg2}, @dots{}) :- @var{Goal}), @var{DB0}, @var{DB}).
+
+aditi_bulk_delete((@var{FuncName}(@var{Arg1}, @var{Arg2}, @dots{}) = @var{RetVal} :- @var{Goal}), @var{DB0}, @var{DB}).
+
 aditi_bulk_delete(@var{PredOrFunc} @var{Name}/@var{Arity}, @var{Closure}, @var{DB0}, @var{DB}).
 @end example
 
-Delete all solutions of @samp{@var{Closure}} from the named relation.
+ at sp 1
 
+Delete all solutions of @samp{@var{Goal}} or @samp{@var{Closure}}
+from the named relation.
+
+ at sp 1
+
 @itemize @bullet
 @item
 @samp{@var{PredOrFunc}} must be either @samp{pred} or @samp{func}.
@@ -6418,6 +6351,9 @@
 being updated.
 
 @item
+ at samp{@var{Goal}} must be a Mercury goal.
+
+ at item
 @samp{@var{Closure}} must be a data-term which has a higher-order type with
 the same type signature as the base relation being updated.
 
@@ -6436,25 +6372,88 @@
 @samp{aditi__state}. They have mode @w{@samp{aditi_di, aditi_uo}}.
 @end itemize
 
+ at c Don't delete the blank lines here -- they are needed for readability.
+ at c @sp commands have no effect on the info file.
+ at example
+
+
+aditi_bulk_insert((@var{PredName}(@var{DB1}, @var{Arg2}, @dots{}) :- @var{Goal}), @var{DB0}, @var{DB}).
+ at end example 
+
+is equivalent to 
+
+ at example
+Closure = (aditi_bottom_up
+        pred(@var{DB1}::aditi_ui, @var{Arg2}::out, @dots{}) is nondet :- @var{Goal}),
+aditi_bulk_insert(@var{PredOrFunc} @var{Name}/@var{Arity}, @var{Closure}, @var{DB0}, @var{DB}).
+ at end example 
+
+bulk_insert_example_1, bulk_insert_example_2 and bulk_insert_example_3 below
+are all equivalent.
+
+ at c Don't delete the blank lines here -- they are needed for readability.
+ at c @sp commands have no effect on the info file.
+ at example
+
+
+aditi_bulk_delete((@var{PredName}(@var{Arg1}, @var{Arg2}, @dots{}) :- @var{Goal}), @var{DB0}, @var{DB}).
+ at end example 
+
+is equivalent to
+
+ at example
+DeleteClosure = (aditi_bottom_up
+        pred(@var{DB1}::aditi_ui, @var{Arg2}::out, @dots{}) is nondet :-
+        @var{PredName}(@var{DB1}, @var{Arg2}, @dots{}),
+        @var{Goal}
+),
+aditi_bulk_delete(@var{PredOrFunc} @var{Name}/@var{Arity}, @var{Closure}, @var{DB0}, @var{DB}).
+ at end example 
+
+bulk_delete_example_1 and bulk_delete_example_2 below are equivalent.
+
+ at sp 2
+
 Examples:
 @example
 bulk_insert_example_1(DB0, DB) :-
-        aditi_bulk_insert(pred p/3, ancestor, DB0, DB).
-
-bulk_delete_example_1(DB0, DB) :-
-        aditi_bulk_delete(pred p/3, ancestor, DB0, DB).
+        aditi_bulk_insert(p(DB1, X, Y) :- ancestor(DB1, X, Y), DB0, DB).
 
 bulk_insert_example_2(DB0, DB) :-
+        aditi_bulk_insert(pred p/3, ancestor, DB0, DB).
+
+bulk_insert_example_3(DB0, DB) :-
         InsertP = (aditi_bottom_up
                 pred(DB1::aditi_ui, X::out, Y::out) is nondet :-
                         ancestor(DB1, X, Y)
                 ),
         aditi_bulk_insert(pred p/3, InsertP, DB0, DB).
 
+bulk_delete_example_1 -->
+        aditi_bulk_delete(
+                (p(DB1, X, _) :-
+                        X > 1,
+                        X < 5
+                )).
+
 bulk_delete_example_2(DB0, DB) :-
+        DeleteP = (aditi_bottom_up
+                pred(DB1::aditi_ui, X::out, Y::out) is nondet :-
+                        p(DB1, X, Y),
+                        X > 1,
+                        X < 5
+                ),
+        aditi_bulk_delete(DeleteP, DB0, DB).
+
+bulk_delete_example_3(DB0, DB) :-
+        aditi_bulk_delete(f(DB1, X) = _Y :- X = 1, DB0, DB).
+
+bulk_delete_example_4(DB0, DB) :-
         DeleteQ = (aditi_bottom_up
                 func(DB1::aditi_ui, X::out) = (Y::out) is nondet :-
-                        ancestor(DB1, X, Y)
+                        q(DB1, X) = Y,
+                        X > 1,
+                        X < 5
                 ),
         aditi_bulk_delete(func f/2, DeleteQ, DB0, DB).
 @end example
@@ -6464,31 +6463,39 @@
 Its inst is @w{@samp{pred(aditi_ui, out, out) is nondet}},
 as for a normal lambda expression.
 
+Note that in @samp{bulk_delete_example_1} the extra set of parentheses around
+the goal are needed, otherwise the second goal in the conjunction in the
+deletion goal would be parsed as an extra argument
+of the @samp{aditi_bulk_delete} call, resulting in a syntax error.
+
 @node Modification
 @subsubsection Modification
 
 @example
-aditi_modify(
+aditi_bulk_modify(
         (@var{PredName}(@var{OldArg1}, @var{OldArg2}, @dots{}) ==>
         @var{PredName}(@var{NewArg1}, @var{NewArg2}, @dots{}) :-
                 @var{Goal}
         ),
         @var{DB0}, @var{DB}).
 
-aditi_modify(
+aditi_bulk_modify(
         ((@var{FuncName}(@var{OldArg1}, @var{OldArg2}, @dots{}) = @var{OldRetVal}) ==>
         (@var{FuncName}(@var{NewArg1}, @var{NewArg2}, @dots{}) = @var{NewRetVal}) :-
                 @var{Goal}
         ),
         @var{DB0}, @var{DB}).
-
-aditi_modify(@var{PredOrFunc} @var{PredName}/@var{Arity}, @var{Closure}, @var{DB0}, @var{DB}).
 
+aditi_bulk_modify(@var{PredOrFunc} @var{PredName}/@var{Arity}, @var{Closure}, @var{DB0}, @var{DB}).
 @end example
 
+ at sp 1
+
 Modify tuples for which @samp{@var{Goal}} or @samp{@var{Closure}} succeeds,
 leaving any other tuples unchanged.
 
+ at sp 1
+
 @itemize @bullet
 @item
 @samp{@var{PredName}} must be the name of a predicate. 
@@ -6512,10 +6519,11 @@
 must be data-terms.
 
 The original tuple is given by the first set of arguments, which
-have mode @samp{in}. The updated tuple is given by the second set
+have mode @samp{out}. The updated tuple is given by the second set
 of arguments, which have mode @samp{out}. The @samp{aditi__state}
-arguments for both tuples have mode @samp{unused} --- it is not possible
-to call an Aditi relation from the modification goal.
+argument for the original tuple has mode @samp{aditi_ui}.
+The @samp{aditi__state} argument for the updated tuple has mode
+ at samp{unused}.
 
 The argument types of each tuple must match the argument types
 of the base relation being modified.
@@ -6529,68 +6537,130 @@
 When modifying a predicate with type declaration
 @w{@samp{:- pred p(aditi__state, @var{Type1}, @dots{})}}, @samp{@var{Closure}}
 must have type
- at samp{aditi_top_down pred(aditi__state, @var{Type1}, @dots{}, aditi__state, @var{Type1}, @dots{})},
-and inst @w{@samp{pred(unused, in, @dots{}, unused, out, @dots{}) is semidet}}.
+ at samp{aditi_bottom_up pred(aditi__state, @var{Type1}, @dots{}, aditi__state, @var{Type1}, @dots{})},
+and inst
+ at w{@samp{pred(aditi_ui, out, @dots{}, unused, out, @dots{}) is nondet}}.
 
 When modifying a function with type declaration
 @w{@samp{:- func p(aditi__state, @var{Type1}, @dots{}) = @var{Type2}}},
 @samp{@var{Closure}} must have type
- at samp{aditi_top_down pred(aditi__state, @var{Type1}, @dots{}, @var{Type2}, aditi__state, @var{Type1}, @dots{}, @var{Type2})},
+ at samp{aditi_bottom_up pred(aditi__state, @var{Type1}, @dots{}, @var{Type2}, aditi__state, @var{Type1}, @dots{}, @var{Type2})},
 and inst
- at w{@samp{pred(unused, in, @dots{}, in, unused, out, @dots{}, out) is semidet}}.
+ at w{@samp{pred(aditi_ui, out, @dots{}, out, unused, out, @dots{}, out) is nondet}}.
 
-The @samp{aditi__state} arguments of @samp{@var{Closure}} must have
-mode @samp{unused} --- it is not possible to call an Aditi
-relation from the modification goal.
-
-If the construction of @samp{@var{Closure}} is in the same conjunction
-as the @samp{aditi_modify} call, the compiler may be able to do a better
-job of optimizing the modification using indexes.
+It is an error for the closure to return a solution for which the arguments
+corresponding to the original tuple do not match a tuple in the relation
+being modified.
 
 @item
 @samp{@var{DB0}} and @samp{@var{DB}} must be data-terms of type
 @samp{aditi__state}. They have mode @w{@samp{aditi_di, aditi_uo}}.
 @end itemize
 
+ at sp 2
+
+The first syntax can be rewritten into the second:
+
+ at example
+aditi_bulk_modify(
+        (@var{PredName}(@var{DB1}, @var{OldArg1}, @var{OldArg2}, @dots{}) ==>
+        @var{PredName}(@var{_DB}, @var{NewArg1}, @var{NewArg2}, @dots{}) :-
+                @var{Goal}
+        ),
+        @var{DB0}, @var{DB}).
+ at end example
+
+is equivalent to
+
+ at example
+ModifyClosure =
+        (aditi_bottom_up pred(@var{DB1}::aditi_ui, @var{OldArg1}::out, @var{OldArg2}::out, @dots{},
+                @var{_DB}::unused, @var{NewArg1}::out, @var{NewArg2}::out, @dots{}) is nondet :-
+                @var{PredName}(@var{DB1}, @var{OldArg1}, @var{OldArg2}, @dots{}),
+                @var{Goal}
+        ),
+aditi_bulk_modify(pred @var{PredName}/@var{PredArity}, ModifyClosure, DB0, DB).
+ at end example
+
+ at c Don't delete the blank lines here -- they are needed for readability.
+ at c @sp commands have no effect on the info file.
+ at example
+
+
+aditi_bulk_modify(pred p/3, Closure, DB0, DB).
+ at end example
+
+is almost equivalent to
+
+ at example
+DeleteClosure =
+        (aditi_bottom_up pred(DB1::aditi_ui, X1::out, Y1::out) is nondet :-
+                Closure(DB1, X1, Y1, _, _)
+        ),
+InsertClosure =
+        (aditi_bottom_up pred(DB1::aditi_ui, X2::out, Y2::out) is nondet :-
+                Closure(DB1, _, _, X2, Y2)
+        ),
+aditi_bulk_delete(pred p/3, DeleteClosure, DB0, DB1),
+aditi_bulk_insert(pred p/3, InsertClosure, DB1, DB).
+ at end example
+
+They are not quite equivalent because @var{InsertClosure}
+is executed using the contents of @samp{p/3} before the deletion
+is applied.
+
+ at sp 2
+
 Examples:
 @example
-modify_example_1(DB0, DB) :-
-        aditi_modify(
-                (p(_DB0, X, Y0) ==> p(_DB, X, Y) :-
-                        X > 2, X < 5, Y = Y0 + 1
+bulk_modify_example_1(DB0, DB) :-
+        aditi_bulk_modify(
+                (p(DB1, X, Y0) ==> p(_DB, X, Y) :-
+                        X > 2,
+                        X < 5,
+                        Y = Y0 + 1
                 ), DB0, DB).
 
-modify_example_2(DB0, DB) :-
-        aditi_modify(
-                ((f(_DB0, X) = Y0) ==> (f(_DB, X) = Y) :-
+bulk_modify_example_2(DB0, DB) :-
+        aditi_bulk_modify(
+                (f(_DB0, X) = Y0 ==> f(_DB, X) = Y :-
                         X > 2, X < 5, Y = Y0 + 1
                 ), DB0, DB).
 
-modify_example_3(DB0, DB) :-
-        ModifyP = (aditi_top_down pred(_::unused, X::in, Y0::in,
-                        _::unused, X::out, Y::out) is semidet :-
-                    X > 2, X < 5, Y = Y0 + 1
+bulk_modify_example_3(DB0, DB) :-
+        ModifyP = (aditi_bottom_up pred(DB1::aditi_ui, X::in, Y0::in,
+                        _::unused, X::out, Y::out) is nondet :-
+                    p(DB1, X, Y0),
+                    X > 2,
+                    X < 5,
+                    Y = Y0 + 1
                  ),
-        aditi_modify(pred p/3, ModifyP, DB0, DB).
+        aditi_bulk_modify(pred p/3, ModifyP, DB0, DB).
 
-modify_example_4(DB0, DB) :-
-        ModifyQ = (aditi_top_down pred(_::unused, X::in, Y0::in,
-                        _::unused, X::out, Y::out) is semidet :-
+bulk_modify_example_4(DB0, DB) :-
+        ModifyF = (aditi_bottom_up pred(DB1::aditi_ui, X::in, Y0::in,
+                        _::unused, X::out, Y::out) is nondet :-
+                    f(DB1, X) = Y0,
                     X > 2, X < 5, Y = Y0 + 1
                  ),
-        aditi_modify(func f/2, ModifyQ, DB0, DB).
+        aditi_bulk_modify(func f/2, ModifyQ, DB0, DB).
 
-modify_example_5 -->
-        aditi_modify(
+bulk_modify_example_5 -->
+        aditi_bulk_modify(
                 (p(_DB0, X, Y0) ==> p(_DB, X, Y) :-
                         X > 2, X < 5, Y = Y0 + 1
                 )).
 @end example
 
-Note that in @samp{modify_example_5} the extra set of parentheses around
+Note that in @samp{bulk_modify_example_5} the extra set of parentheses around
 the goal are needed, otherwise the second and third goals in
 the conjunction in the modification goal would be parsed as extra arguments
-of the @samp{aditi_modify} call, resulting in a syntax error.
+of the @samp{aditi_bulk_modify} call, resulting in a syntax error.
+
+The type of @samp{ModifyP} is
+ at w{@samp{aditi_bottom_up pred(aditi__state, int, int, aditi__state, int, int)}}.
+Its inst is @w{@samp{pred(aditi_ui, out, out, unused, out, out) is nondet}},
+as for a normal lambda expression.
 
 @node Aditi glossary
 @subsection Aditi glossary

Index: compiler/call_gen.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/call_gen.m,v
retrieving revision 1.138
diff -u -u -r1.138 call_gen.m
--- compiler/call_gen.m	2000/03/15 08:30:52	1.138
+++ compiler/call_gen.m	2000/04/11 02:42:06
@@ -57,7 +57,7 @@
 
 :- import_module hlds_module, hlds_data, code_util, builtin_ops, rl.
 :- import_module arg_info, type_util, mode_util, unify_proc, instmap.
-:- import_module trace, globals, options.
+:- import_module polymorphism, trace, globals, options.
 :- import_module std_util, bool, int, tree, map.
 :- import_module varset, require, string.
 
@@ -144,9 +144,29 @@
 	% the runtime system leaves them in.
 	%
 
-call_gen__generate_generic_call(_OuterCodeModel, GenericCall, Args,
-		Modes, Det, GoalInfo, Code) -->
-	list__map_foldl(code_info__variable_type, Args, Types),
+call_gen__generate_generic_call(_OuterCodeModel, GenericCall, Args0,
+		Modes0, Det, GoalInfo, Code) -->
+	list__map_foldl(code_info__variable_type, Args0, Types0),
+
+	{ GenericCall = aditi_builtin(aditi_tuple_insert_delete(_, _), _) ->
+		% Remove the `aditi__state' argument and its type-info from
+		% the tuple to insert or delete. This must be done after
+		% mode analysis (so that removal of the `aditi__state' does
+		% not stuff up the argument numbers in error messages).
+		% Here is as good a place as any.
+		get_state_args_det(Types0, TupleTypes, _, _),
+		call_gen__remove_tuple_state_arg(TupleTypes,
+			Args0, Args),
+		call_gen__remove_tuple_state_arg(TupleTypes,
+			Types0, Types),
+		call_gen__remove_tuple_state_arg(TupleTypes,
+			Modes0, Modes)
+	;
+		Args = Args0,
+		Types = Types0,
+		Modes = Modes0
+	},
+
 	{ determinism_to_code_model(Det, CodeModel) },
 	code_info__get_module_info(ModuleInfo),
 	{ make_arg_infos(Types, Modes, CodeModel, ModuleInfo, ArgInfos) },
@@ -219,6 +239,24 @@
 		     FailHandlingCode))))))
 	}.
 
+:- pred call_gen__remove_tuple_state_arg(list(type), list(T), list(T)).
+:- mode call_gen__remove_tuple_state_arg(in, in, out) is det.
+
+call_gen__remove_tuple_state_arg(TupleTypes, Args0, Args) :-
+	get_state_args_det(Args0, OtherArgs0, State0Arg, StateArg),
+	assoc_list__from_corresponding_lists(TupleTypes, OtherArgs0,
+		TypesAndArgs0),
+	list__filter(
+		(pred((Type - _)::in) is semidet :-
+			\+ type_is_aditi_state(Type),
+			\+ (
+				polymorphism__type_info_type(Type, TheType),
+				type_is_aditi_state(TheType)
+			)
+		), TypesAndArgs0, TypesAndArgs),
+	assoc_list__values(TypesAndArgs, OtherArgs),
+	list__append(OtherArgs, [State0Arg, StateArg], Args).
+
 	% The registers before the first input argument are all live.
 :- pred call_gen__extra_livevals(int, list(lval)).
 :- mode call_gen__extra_livevals(in, out) is det.
@@ -252,23 +290,37 @@
 	; CodeModel = model_semi, CodeAddr = do_semidet_aditi_call
 	; CodeModel = model_non, CodeAddr = do_nondet_aditi_call
 	).
-call_gen__generic_call_info(CodeModel, aditi_builtin(aditi_insert(_), _),
-		do_aditi_insert, 3) :-
-	require(unify(CodeModel, model_det), "aditi_insert not model_det").
-call_gen__generic_call_info(CodeModel, aditi_builtin(aditi_delete(_,_), _),
-		do_aditi_delete, 2) :-
-	require(unify(CodeModel, model_det), "aditi_delete not model_det").
 call_gen__generic_call_info(CodeModel,
-		aditi_builtin(aditi_bulk_operation(BulkOp, _), _),
-		CodeAddr, 2) :-
-	( BulkOp = insert, CodeAddr = do_aditi_bulk_insert
-	; BulkOp = delete, CodeAddr = do_aditi_bulk_delete
+		aditi_builtin(aditi_tuple_insert_delete(InsertDelete, _), _),
+		CodeAddr, 5) :-
+	( InsertDelete = insert, CodeAddr = do_aditi_insert
+	; InsertDelete = delete, CodeAddr = do_aditi_delete
 	),
+	require(unify(CodeModel, model_det),
+		"aditi_insert/delete not model_det").
+call_gen__generic_call_info(CodeModel,
+		aditi_builtin(
+			aditi_insert_delete_modify(InsertDelMod, _, _), _),
+		CodeAddr, FirstReg) :-
+	call_gen__aditi_insert_delete_modify_info(InsertDelMod,
+		CodeAddr, FirstReg),
 	require(unify(CodeModel, model_det),
-		"aditi_bulk_operation not model_det").
-call_gen__generic_call_info(CodeModel, aditi_builtin(aditi_modify(_,_), _),
-		do_aditi_modify, 2) :-
-	require(unify(CodeModel, model_det), "aditi_modify not model_det").
+		"aditi_insert_delete_modify not model_det").
+
+:- pred call_gen__aditi_insert_delete_modify_info(aditi_insert_delete_modify,
+		code_addr, int).
+:- mode call_gen__aditi_insert_delete_modify_info(in, out, out) is det.
+
+call_gen__aditi_insert_delete_modify_info(bulk_insert,
+		do_aditi_bulk_insert, 3).
+call_gen__aditi_insert_delete_modify_info(delete(filter), _, _) :-
+	error("Sorry, not yet implemented: aditi_delete(filter)").
+call_gen__aditi_insert_delete_modify_info(delete(bulk),
+		do_aditi_bulk_delete, 3).
+call_gen__aditi_insert_delete_modify_info(modify(filter), _, _) :-
+	error("Sorry, not yet implemented: aditi_modify(filter)").
+call_gen__aditi_insert_delete_modify_info(modify(bulk),
+		do_aditi_bulk_modify, 3).
 
 	% Produce code to set up the arguments to a generic call
 	% that are always present, such as the closure for a higher-order call,
@@ -349,23 +401,94 @@
 		assign(reg(r, 4), const(int_const(NumOutputs))) -
 			"Assign number of output arguments"
 	]) }.
-call_gen__aditi_builtin_setup(aditi_insert(PredId), Inputs, _, SetupCode) -->
+
+call_gen__aditi_builtin_setup(
+		aditi_tuple_insert_delete(InsertOrDelete, PredId),
+		InputArgs, _, SetupCode) -->
 	call_gen__setup_base_relation_name(PredId, NameCode),
-	{ list__length(Inputs, NumInputs) },
-	{ SetupCode =
-		tree(NameCode,
-		node([
-			assign(reg(r, 2), const(int_const(NumInputs))) -
+
+	code_info__get_module_info(ModuleInfo),
+	{ module_info_pred_info(ModuleInfo, PredId, PredInfo) },
+	{ pred_info_arity(PredInfo, PredArity) },
+	% The `aditi__state' was removed.
+	{ TupleArity = PredArity - 1 },
+	{ ArityCode = node([
+			assign(reg(r, 2), const(int_const(TupleArity))) -
 				"Assign arity of relation to insert into"
-		])
-	) }.
-call_gen__aditi_builtin_setup(aditi_delete(PredId, _), _, _, SetupCode) -->
-	call_gen__setup_base_relation_name(PredId, SetupCode).
-call_gen__aditi_builtin_setup(aditi_bulk_operation(_, PredId), _, _,
-		SetupCode) -->
-	call_gen__setup_base_relation_name(PredId, SetupCode).
-call_gen__aditi_builtin_setup(aditi_modify(PredId, _), _, _, SetupCode) -->
+		]) },
+
+	(
+		{ InsertOrDelete = insert },
+		{ ProcCode = empty }
+	;
+		{ InsertOrDelete = delete },
+
+		%
+		% For now tuple deletions need to be done as bulk
+		% deletions. The API function to delete a single
+		% tuple only works if the relation being
+		% deleted from has an index.
+		%
+
+		call_gen__setup_update_proc_name(rl__get_delete_proc_name,
+			PredId, reg(r, 3), ProcNameCode),
+
+		%
+		% Work out the schema of the input relation of the
+		% deletion procedure
+		%
+		{ list__reverse(InputArgs, RevInputArgs) },
+		{
+			RevInputArgs = [_DiState | RevTupleArgs],
+			list__reverse(RevTupleArgs, TupleArgs0),
+			list__length(TupleArgs0, TupleArityTimes2),
+
+			% Remove the type-infos for the tuple arguments.
+			list__drop(TupleArityTimes2 // 2,
+				TupleArgs0, TupleArgs1)
+		->
+			TupleArgs = TupleArgs1
+		;
+			error(
+	"call_gen__aditi_builtin_setup: error in schema for aditi_delete")
+		},
+		list__map_foldl(code_info__variable_type,
+			TupleArgs, TupleTypes),
+		{ rl__schema_to_string(ModuleInfo, TupleTypes, InputSchema) },
+		{ ProcSchemaCode =
+			node([
+				assign(reg(r, 4),
+					const(string_const(InputSchema))) -
+				"Assign schema of tuple to insert/delete"
+			]) },
+
+		{ ProcCode = tree(ProcNameCode, ProcSchemaCode) }
+	),
+	{ SetupCode = tree(NameCode, tree(ArityCode, ProcCode)) }.
+
+call_gen__aditi_builtin_setup(
+		aditi_insert_delete_modify(InsertDelMod, PredId, _),
+		_, _, SetupCode) -->
+	call_gen__aditi_insert_delete_modify_setup(InsertDelMod,
+		PredId, SetupCode).
+
+:- pred call_gen__aditi_insert_delete_modify_setup(aditi_insert_delete_modify,
+		pred_id, code_tree, code_info, code_info).
+:- mode call_gen__aditi_insert_delete_modify_setup(in, in, out,
+		in, out) is det.
+
+call_gen__aditi_insert_delete_modify_setup(bulk_insert, PredId, SetupCode) -->
 	call_gen__setup_base_relation_name(PredId, SetupCode).
+call_gen__aditi_insert_delete_modify_setup(delete(_), PredId, SetupCode) -->
+	call_gen__setup_base_relation_name(PredId, RelNameCode),
+	call_gen__setup_update_proc_name(rl__get_delete_proc_name,
+		PredId, reg(r, 2), ProcNameCode),
+	{ SetupCode = tree(RelNameCode, ProcNameCode) }.
+call_gen__aditi_insert_delete_modify_setup(modify(_), PredId, SetupCode) -->
+	call_gen__setup_base_relation_name(PredId, RelNameCode),
+	call_gen__setup_update_proc_name(rl__get_modify_proc_name,
+		PredId, reg(r, 2), ProcNameCode),
+	{ SetupCode = tree(RelNameCode, ProcNameCode) }.
 
 :- pred call_gen__setup_base_relation_name(pred_id,
 		code_tree, code_info, code_info).
@@ -378,6 +501,24 @@
 		assign(reg(r, 1), const(string_const(ProcStr))) -
 			"Assign name of base relation"
 	]) }.
+
+:- pred call_gen__setup_update_proc_name(
+		pred(module_info, pred_id, rl_proc_name),
+		pred_id, lval, code_tree, code_info, code_info).
+:- mode call_gen__setup_update_proc_name(pred(in, in, out) is det,
+		in, in, out, in, out) is det.
+
+call_gen__setup_update_proc_name(NamePred, PredId, Lval, ProcNameCode) -->
+	code_info__get_module_info(ModuleInfo),
+	{ NamePred(ModuleInfo, PredId, ProcName) },
+	{ rl__proc_name_to_string(ProcName, ProcNameStr) },
+	{ ProcNameCode =
+		node([
+			assign(Lval,
+				const(string_const(ProcNameStr))) -
+				"Assign name of update RL procedure"
+		])
+	}.
 
 %---------------------------------------------------------------------------%
 
Index: compiler/code_util.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/code_util.m,v
retrieving revision 1.118
diff -u -u -r1.118 code_util.m
--- compiler/code_util.m	2000/03/15 08:30:53	1.118
+++ compiler/code_util.m	2000/04/12 04:51:15
@@ -54,6 +54,15 @@
 :- pred code_util__make_proc_label(module_info, pred_id, proc_id, proc_label).
 :- mode code_util__make_proc_label(in, in, in, out) is det.
 
+	% code_util__make_user_proc_label(ModuleName, ImportStatus,
+	%	PredOrFunc, ModuleName, PredName, Arity, ProcId, Label)
+	%
+	% Make a proc_label for a user-defined procedure.
+:- pred code_util__make_user_proc_label(module_name, import_status,
+	pred_or_func, module_name, string, arity, proc_id, proc_label).
+:- mode code_util__make_user_proc_label(in, in,
+	in, in, in, in, in, out) is det.
+
 :- pred code_util__make_uni_label(module_info, type_id, proc_id, proc_label).
 :- mode code_util__make_uni_label(in, in, in, out) is det.
 
@@ -272,23 +281,32 @@
 			error(ErrorMessage)
 		)
 	;
-		(
-			% Work out which module supplies the code for
-			% the predicate.
-			ThisModule \= PredModule,
-			\+ pred_info_is_imported(PredInfo)
-		->
-			% This predicate is a specialized version of 
-			% a pred from a `.opt' file.
-			DefiningModule = ThisModule
-		;	
-			DefiningModule = PredModule
-		),
 		pred_info_get_is_pred_or_func(PredInfo, PredOrFunc),
-		pred_info_arity(PredInfo, Arity),
-		ProcLabel = proc(DefiningModule, PredOrFunc,
-			PredModule, PredName, Arity, ProcId)
+		pred_info_import_status(PredInfo, ImportStatus),
+		pred_info_arity(PredInfo, PredArity),
+		code_util__make_user_proc_label(ThisModule, ImportStatus,
+			PredOrFunc, PredModule, PredName, PredArity,
+			ProcId, ProcLabel)
 	).
+
+code_util__make_user_proc_label(ThisModule, ImportStatus,
+		PredOrFunc, PredModule, PredName, PredArity,
+		ProcId, ProcLabel) :-
+
+	(
+		% Work out which module supplies the code for
+		% the predicate.
+		ThisModule \= PredModule,
+		ImportStatus \= imported(_)
+	->
+		% This predicate is a specialized version of 
+		% a pred from a `.opt' file.
+		DefiningModule = ThisModule
+	;	
+		DefiningModule = PredModule
+	),
+	ProcLabel = proc(DefiningModule, PredOrFunc,
+		PredModule, PredName, PredArity, ProcId).
 
 code_util__make_uni_label(ModuleInfo, TypeId, UniModeNum, ProcLabel) :-
 	module_info_name(ModuleInfo, ModuleName),
Index: compiler/context.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/context.m,v
retrieving revision 1.2
diff -u -u -r1.2 context.m
--- compiler/context.m	2000/02/21 00:05:02	1.2
+++ compiler/context.m	2000/03/27 03:11:32
@@ -503,9 +503,12 @@
 		{ map__lookup(MagicProcInfo, proc(PredId, ProcId), 
 			ThisProcInfo) },
 		{ ThisProcInfo = magic_proc_info(OldArgModes, _, _, _, _) },
-		magic_util__create_input_test_unifications(Args, InputArgs,
-			OldArgModes, NewArgs, [], Tests,
-			GoalInfo0, GoalInfo1),
+		magic_info_get_module_info(ModuleInfo0),
+		magic_info_get_proc_info(ProcInfo0),
+		{ magic_util__create_input_test_unifications(ModuleInfo0,
+			Args, InputArgs, OldArgModes, NewArgs, [], Tests,
+			GoalInfo0, GoalInfo1, ProcInfo0, ProcInfo) },
+		magic_info_set_proc_info(ProcInfo),
 		{ goal_info_get_nonlocals(GoalInfo1, GoalNonLocals1) },
 		magic_util__restrict_nonlocals(GoalNonLocals1, GoalNonLocals),
 		{ goal_info_set_nonlocals(GoalInfo1,
Index: compiler/exprn_aux.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/exprn_aux.m,v
retrieving revision 1.38
diff -u -u -r1.38 exprn_aux.m
--- compiler/exprn_aux.m	2000/01/14 01:10:18	1.38
+++ compiler/exprn_aux.m	2000/03/17 03:15:08
@@ -160,7 +160,7 @@
 exprn_aux__addr_is_constant(do_aditi_delete, _, no).
 exprn_aux__addr_is_constant(do_aditi_bulk_insert, _, no).
 exprn_aux__addr_is_constant(do_aditi_bulk_delete, _, no).
-exprn_aux__addr_is_constant(do_aditi_modify, _, no).
+exprn_aux__addr_is_constant(do_aditi_bulk_modify, _, no).
 exprn_aux__addr_is_constant(do_not_reached, _, no).
 
 :- pred exprn_aux__label_is_constant(label, bool, bool, bool).
Index: compiler/hlds_goal.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/hlds_goal.m,v
retrieving revision 1.68
diff -u -u -r1.68 hlds_goal.m
--- compiler/hlds_goal.m	2000/02/10 04:47:39	1.68
+++ compiler/hlds_goal.m	2000/03/21 00:08:36
@@ -241,44 +241,73 @@
 			int		% number of outputs
 		)
 
-		% Insert a single tuple into a predicate.
+		% Insert or delete a single tuple into/from a base relation.
 		% Arguments:
 		%   type-infos for the arguments of the tuple to insert
 		%   the arguments of tuple to insert
 		% aditi__state::di, aditi__state::uo
-	;	aditi_insert(
+	;	aditi_tuple_insert_delete(
+			aditi_insert_delete,
 			pred_id		% base relation to insert into
 		)
 
-		% Apply a filter to a relation.
-		% Arguments:
-		%   deletion condition (semidet `aditi_top_down' closure). 
-		%   aditi__state::di, aditi__state::uo
-	;	aditi_delete(
-			pred_id,	% base relation to delete from
+		% Insert/delete/modify operations which take
+		% an input closure.
+		% These operations all have two variants. 
+		%
+		% A pretty syntax:
+		%
+		% aditi_bulk_insert(p(DB, X, Y) :- q(DB, X, Y)).
+		% aditi_bulk_delete(p(DB, X, Y) :- q(DB, X, Y)).
+		% aditi_bulk_modify(
+		%	(p(DB, X0, Y0) ==> p(_, X, Y) :-
+		%		X = X0 + 1,
+		%		Y = Y0 + 3
+		%	)).
+		%
+		% An ugly syntax:
+		%
+		% InsertPred = (aditi_bottom_up
+		%	pred(DB::aditi_ui, X::out, Y::out) :- 
+		%		q(DB, X, Y)
+		% ),
+		% aditi_bulk_insert(pred p/3, InsertPred).
+		%
+		% DeletePred = (aditi_bottom_up
+		%	pred(DB::aditi_ui, X::out, Y::out) :- 
+		%		p(DB, X, Y),
+		%		q(DB, X, Y)
+		% ),
+		% aditi_bulk_delete(pred p/3, DeletePred).
+	;	aditi_insert_delete_modify(
+			aditi_insert_delete_modify,
+			pred_id,
 			aditi_builtin_syntax
 		)
+	.
 
-		% Insert or delete the tuples returned by a query.
-		% Arguments:
-		%   query to generate tuples to insert or delete
-		% 	(nondet `aditi_bottom_up' closure).
-		%   aditi__state::di, aditi__state::uo
-	;	aditi_bulk_operation(
-			aditi_bulk_operation,
-			pred_id		% base relation to insert into
-		)
+:- type aditi_insert_delete
+	--->	delete			% `aditi_delete'
+	;	insert			% `aditi_insert'
+	.
 
-		% Modify the tuples in a relation.
-		% Arguments:
-		%   semidet `aditi_top_down' closure to construct a
-		%	new tuple from the old tuple.
-		%	The tuple is not changed if the closure fails.
- 		%   aditi__state::di, aditi__state::uo.
-	;	aditi_modify(
-			pred_id,	% base relation to modify
-			aditi_builtin_syntax
-		)
+:- type aditi_insert_delete_modify
+	--->	delete(bulk_or_filter)	% `aditi_bulk_delete' or `aditi_filter'
+	;	bulk_insert		% `aditi_bulk_insert'
+	;	modify(bulk_or_filter)	% `aditi_bulk_modify' or `aditi_modify'
+	.
+
+	% Deletions and modifications can either be done by computing
+	% all tuples for which the update applies, then applying the
+	% update for all tuples in one go (`bulk'), or by applying
+	% the update to each tuple during a pass over the relation
+	% being modified (`filter').
+	%
+	% The `filter' updates are not yet implemented in Aditi, and
+	% it may be difficult to ever implement them.
+:- type bulk_or_filter
+	--->	bulk
+	;	filter
 	.
 
 	% Which syntax was used for an `aditi_delete' or `aditi_modify'
@@ -287,17 +316,13 @@
 	% (See the "Aditi update syntax" section of the Mercury Language
 	% Reference Manual).
 :- type aditi_builtin_syntax
-	--->	pred_term		% e.g.	aditi_delete(p(_, X) :- X = 1).
+	--->	pred_term		% e.g.
+					% aditi_bulk_insert(p(_, X) :- X = 1).
 	;	sym_name_and_closure	% e.g.
-					% aditi_delete(p/2,
-					%    (pred(_::in, X::in) is semidet :-
+					% aditi_insert(p/2,
+					%    (pred(_::in, X::out) is nondet:-
 					%	X = 1)
 					%    )
-	.
-
-:- type aditi_bulk_operation
-	--->	insert
-	;	delete
 	.
 
 :- type can_remove
Index: compiler/hlds_out.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/hlds_out.m,v
retrieving revision 1.236
diff -u -u -r1.236 hlds_out.m
--- compiler/hlds_out.m	2000/03/28 03:40:20	1.236
+++ compiler/hlds_out.m	2000/03/31 00:45:47
@@ -479,47 +479,36 @@
 	io__write_string("argument "),
 	io__write_int(ArgNum).
 
-hlds_out__write_aditi_builtin_arg_number(aditi_insert(_),
+hlds_out__write_aditi_builtin_arg_number(
+		aditi_tuple_insert_delete(InsertDelete, _),
 		_ - _/Arity, ArgNum) -->
 	io__write_string("argument "),
 	( { ArgNum =< Arity } ->
 		io__write_int(ArgNum),
-		io__write_string(" of the inserted tuple")
+		io__write_string(" of the "),
+		{ InsertDelete = insert, Str = "inserted"
+		; InsertDelete = delete, Str = "deleted"
+		},
+		io__write_string(Str),
+		io__write_string(" tuple")
 	;
 		io__write_int(ArgNum - Arity + 1)
 	).
 
-hlds_out__write_aditi_builtin_arg_number(aditi_delete(_, pred_term),
+hlds_out__write_aditi_builtin_arg_number(
+		aditi_insert_delete_modify(_, _, pred_term),
 		_, ArgNum) -->
 	io__write_string("argument "),
 	io__write_int(ArgNum).
 
-hlds_out__write_aditi_builtin_arg_number(aditi_delete(_, sym_name_and_closure),
+hlds_out__write_aditi_builtin_arg_number(
+		aditi_insert_delete_modify(_, _, sym_name_and_closure),
 		_, ArgNum) -->
 	% The original goal had a sym_name/arity
 	% at the front of the argument list.
 	io__write_string("argument "),
 	io__write_int(ArgNum + 1).
 
-hlds_out__write_aditi_builtin_arg_number(aditi_bulk_operation(_, _),
-		_, ArgNum) -->
-	% The original goal had a sym_name/arity
-	% at the front of the argument list.
-	io__write_string("argument "),
-	io__write_int(ArgNum + 1).
-
-hlds_out__write_aditi_builtin_arg_number(aditi_modify(_, pred_term),
-		_, ArgNum) -->
-	io__write_string("argument "),
-	io__write_int(ArgNum).
-
-hlds_out__write_aditi_builtin_arg_number(aditi_modify(_, sym_name_and_closure),
-		_, ArgNum) -->
-	% The original goal had a sym_name/arity
-	% at the front of the argument list.
-	io__write_string("argument "),
-	io__write_int(ArgNum + 1).	
-
 hlds_out__write_pred_or_func(predicate) -->
 	io__write_string("predicate").
 
@@ -1650,12 +1639,17 @@
 	io__write_string(Follow),
 	io__nl.
 
-hlds_out__write_aditi_builtin(_ModuleInfo, aditi_insert(PredId), CallId,
+hlds_out__write_aditi_builtin(_ModuleInfo,
+		aditi_tuple_insert_delete(InsertDelete, PredId), CallId,
 		ArgVars, VarSet, AppendVarnums, Indent, Follow) -->
 	% make_hlds.m checks the arity so this cannot fail. 
 	{ get_state_args_det(ArgVars, Args, State0Var, StateVar) },
 	hlds_out__write_indent(Indent),	
-	io__write_string("aditi_insert("),
+
+	( { InsertDelete = insert }, io__write_string("aditi_insert(")
+	; { InsertDelete = delete }, io__write_string("aditi_delete(")
+	),
+
 	{ CallId = PredOrFunc - SymName/_ },
 	(
 		{ PredOrFunc = predicate },
@@ -1679,39 +1673,12 @@
 	io__write_string(Follow),
 	io__nl,
 	hlds_out__write_aditi_builtin_pred_id(Indent, PredId).
-
-hlds_out__write_aditi_builtin(_ModuleInfo,
-		aditi_delete(PredId, _), CallId,
-		ArgVars, VarSet, AppendVarnums, Indent, Follow) -->
-	hlds_out__write_aditi_builtin_2("aditi_delete", PredId, CallId,
-		ArgVars, VarSet, AppendVarnums, Indent, Follow).
-
-hlds_out__write_aditi_builtin(_ModuleInfo,
-		aditi_bulk_operation(BulkOp, PredId), CallId,
-		ArgVars, VarSet, AppendVarnums, Indent, Follow) -->
-	{
-		BulkOp = insert,
-		Name = "aditi_bulk_insert"
-	;
-		BulkOp = delete,
-		Name = "aditi_bulk_delete"
-	},
-	hlds_out__write_aditi_builtin_2(Name, PredId, CallId,
-		ArgVars, VarSet, AppendVarnums, Indent, Follow).
-
-hlds_out__write_aditi_builtin(_ModuleInfo, aditi_modify(PredId, _), CallId,
-		ArgVars, VarSet, AppendVarnums, Indent, Follow) -->
-	hlds_out__write_aditi_builtin_2("aditi_modify", PredId, CallId,
-		ArgVars, VarSet, AppendVarnums, Indent, Follow).
-
-:- pred hlds_out__write_aditi_builtin_2(string, pred_id, simple_call_id,
-	list(prog_var), prog_varset, bool, int, string, io__state, io__state).
-:- mode hlds_out__write_aditi_builtin_2(in, in, in, in, in, in, in, in,
-	di, uo) is det.
 
-hlds_out__write_aditi_builtin_2(UpdateName, PredId, CallId,
+hlds_out__write_aditi_builtin(_ModuleInfo, Builtin, CallId,
 		ArgVars, VarSet, AppendVarnums, Indent, Follow) -->
+	{ Builtin = aditi_insert_delete_modify(_, PredId, _Syntax) },
 	hlds_out__write_indent(Indent),	
+	{ hlds_out__aditi_builtin_name(Builtin, UpdateName) },
 	io__write_string(UpdateName),
 	io__write_string("("),
 	{ CallId = PredOrFunc - _ },
@@ -1739,18 +1706,20 @@
 	io__write_string(".\n").
 
 hlds_out__aditi_builtin_name(aditi_call(_, _, _, _), "aditi_call").
-hlds_out__aditi_builtin_name(aditi_insert(_), "aditi_insert").
-hlds_out__aditi_builtin_name(aditi_delete(_, _), "aditi_delete").
-hlds_out__aditi_builtin_name(aditi_bulk_operation(BulkOp, _), Name) :-
-	(
-		BulkOp = insert,
-		Name = "aditi_bulk_insert"
-	;
-		BulkOp = delete,
-		Name = "aditi_bulk_delete"
-	).
-
-hlds_out__aditi_builtin_name(aditi_modify(_, _), "aditi_modify").
+hlds_out__aditi_builtin_name(aditi_tuple_insert_delete(_, _), "aditi_insert").
+hlds_out__aditi_builtin_name(aditi_insert_delete_modify(InsertDelMod, _, _),
+		Name) :-
+	hlds_out__aditi_insert_delete_modify_name(InsertDelMod, Name).
+
+:- pred hlds_out__aditi_insert_delete_modify_name(aditi_insert_delete_modify,
+		string).
+:- mode hlds_out__aditi_insert_delete_modify_name(in, out) is det.
+
+hlds_out__aditi_insert_delete_modify_name(bulk_insert, "aditi_bulk_insert").
+hlds_out__aditi_insert_delete_modify_name(delete(bulk), "aditi_bulk_delete").
+hlds_out__aditi_insert_delete_modify_name(delete(filter), "aditi_delete").
+hlds_out__aditi_insert_delete_modify_name(modify(bulk), "aditi_bulk_modify").
+hlds_out__aditi_insert_delete_modify_name(modify(filter), "aditi_modify").
 
 :- pred hlds_out__write_unification(unification, module_info, prog_varset,
 		inst_varset, bool, int, io__state, io__state).
Index: compiler/livemap.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/livemap.m,v
retrieving revision 1.47
diff -u -u -r1.47 livemap.m
--- compiler/livemap.m	2000/03/20 05:24:50	1.47
+++ compiler/livemap.m	2000/03/23 02:40:42
@@ -417,7 +417,7 @@
 livemap__special_code_addr(do_aditi_delete, no).
 livemap__special_code_addr(do_aditi_bulk_insert, no).
 livemap__special_code_addr(do_aditi_bulk_delete, no).
-livemap__special_code_addr(do_aditi_modify, no).
+livemap__special_code_addr(do_aditi_bulk_modify, no).
 livemap__special_code_addr(do_not_reached, no).
 
 %-----------------------------------------------------------------------------%
Index: compiler/llds.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/llds.m,v
retrieving revision 1.259
diff -u -u -r1.259 llds.m
--- compiler/llds.m	2000/03/20 05:24:51	1.259
+++ compiler/llds.m	2000/03/23 02:40:44
@@ -949,7 +949,7 @@
 	;	do_aditi_delete
 	;	do_aditi_bulk_insert
 	;	do_aditi_bulk_delete
-	;	do_aditi_modify
+	;	do_aditi_bulk_modify
 	;	do_not_reached.		% We should never jump to this address.
 
 	% A proc_label is a label used for the entry point to a procedure.
Index: compiler/llds_out.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/llds_out.m,v
retrieving revision 1.140
diff -u -u -r1.140 llds_out.m
--- compiler/llds_out.m	2000/04/02 08:09:18	1.140
+++ compiler/llds_out.m	2000/04/03 04:58:56
@@ -2788,7 +2788,7 @@
 need_code_addr_decls(do_aditi_delete, yes) --> [].
 need_code_addr_decls(do_aditi_bulk_insert, yes) --> [].
 need_code_addr_decls(do_aditi_bulk_delete, yes) --> [].
-need_code_addr_decls(do_aditi_modify, yes) --> [].
+need_code_addr_decls(do_aditi_bulk_modify, yes) --> [].
 need_code_addr_decls(do_not_reached, yes) --> [].
 
 :- pred output_code_addr_decls(code_addr, io__state, io__state).
@@ -2844,8 +2844,8 @@
 	io__write_string("Declare_entry(do_aditi_bulk_insert);\n").
 output_code_addr_decls(do_aditi_bulk_delete) -->
 	io__write_string("Declare_entry(do_aditi_bulk_delete);\n").
-output_code_addr_decls(do_aditi_modify) -->
-	io__write_string("Declare_entry(do_aditi_modify);\n").
+output_code_addr_decls(do_aditi_bulk_modify) -->
+	io__write_string("Declare_entry(do_aditi_bulk_modify);\n").
 output_code_addr_decls(do_not_reached) -->
 	io__write_string("Declare_entry(do_not_reached);\n").
 
@@ -3106,8 +3106,8 @@
 	io__write_string("tailcall(ENTRY(do_aditi_bulk_delete),\n\t\t"),
 	output_label_as_code_addr(CallerLabel),
 	io__write_string(");\n").
-output_goto(do_aditi_modify, CallerLabel) -->
-	io__write_string("tailcall(ENTRY(do_aditi_modify),\n\t\t"),
+output_goto(do_aditi_bulk_modify, CallerLabel) -->
+	io__write_string("tailcall(ENTRY(do_aditi_bulk_modify),\n\t\t"),
 	output_label_as_code_addr(CallerLabel),
 	io__write_string(");\n").
 output_goto(do_not_reached, CallerLabel) -->
@@ -3193,8 +3193,8 @@
 	io__write_string("ENTRY(do_aditi_bulk_insert)").
 output_code_addr(do_aditi_bulk_delete) -->
 	io__write_string("ENTRY(do_aditi_bulk_delete)").
-output_code_addr(do_aditi_modify) -->
-	io__write_string("ENTRY(do_aditi_modify)").
+output_code_addr(do_aditi_bulk_modify) -->
+	io__write_string("ENTRY(do_aditi_bulk_modify)").
 output_code_addr(do_not_reached) -->
 	io__write_string("ENTRY(do_not_reached)").
 
Index: compiler/magic.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/magic.m,v
retrieving revision 1.15
diff -u -u -r1.15 magic.m
--- compiler/magic.m	2000/04/03 03:42:53	1.15
+++ compiler/magic.m	2000/04/11 04:19:48
@@ -915,8 +915,9 @@
 			PredInfo1, ProcInfo, ModuleInfo) },
 		magic_info_set_module_info(ModuleInfo)
 	;
-		magic__create_input_join_proc(CPredProcId, AditiPredProcId,
-			JoinPredProcId),
+		{ magic__create_input_join_proc(CPredProcId, AditiPredProcId,
+			JoinPredProcId, ModuleInfo1, ModuleInfo) },
+		magic_info_set_module_info(ModuleInfo),
 		
 		%
 		% Create a procedure which is just a synonym
@@ -928,198 +929,135 @@
 	% Make a procedure which calls the Aditi predicate, then joins
 	% the result with the input and projects out the input arguments.
 :- pred magic__create_input_join_proc(pred_proc_id::in, pred_proc_id::in,
-		pred_proc_id::out, magic_info::in, magic_info::out) is det.		
-magic__create_input_join_proc(CPredProcId, AditiPredProcId, JoinPredProcId) -->
-	magic_info_get_module_info(ModuleInfo0),
-	{ module_info_pred_proc_info(ModuleInfo0, CPredProcId,
-		CPredInfo, CProcInfo) },
-	{ proc_info_argmodes(CProcInfo, ArgModes0) },
-	{ pred_info_arg_types(CPredInfo, ArgTypes) },
-	{ type_util__remove_aditi_state(ArgTypes, ArgModes0, ArgModes) },
-	{ partition_args(ModuleInfo0, ArgModes, ArgModes,
-		InputArgModes, OutputArgModes) },
-	(
-		% If the Aditi procedure has no inputs we don't need to
-		% do anything unless it is a base relation.
-		{ InputArgModes = [] },
-		{ \+ hlds_pred__pred_info_is_base_relation(CPredInfo) }
-	->
-		% Set the procedure to exported.
-		{ AditiPredProcId = proc(AditiPredId, _) },
-		{ module_info_pred_info(ModuleInfo0,
-			AditiPredId, AditiPredInfo0) },
-		{ pred_info_set_import_status(AditiPredInfo0,
-			exported, AditiPredInfo) },
-		{ module_info_set_pred_info(ModuleInfo0,
-			AditiPredId, AditiPredInfo, ModuleInfo) },
-		magic_info_set_module_info(ModuleInfo),
-		{ JoinPredProcId = AditiPredProcId }
-	;	
-		{ hlds_pred__pred_info_is_base_relation(CPredInfo) },
-		{ InputArgModes = [] }
-	->
-		% No join with the input is needed for a base relation
-		% with no input arguments.
-		{ proc_info_headvars(CProcInfo, HeadVars0) },
-		{ type_util__remove_aditi_state(ArgTypes,
-			HeadVars0, HeadVars) },
-
-		% Build a call to the original C proc.
-		{ set__list_to_set(HeadVars, NonLocals) },
-		{ instmap_delta_from_mode_list(HeadVars0, ArgModes0,
-			ModuleInfo0, Delta) },
-		{ proc_info_inferred_determinism(CProcInfo, Detism) },
-		{ goal_info_init(NonLocals, Delta, Detism, CallGoalInfo) },
-		{ pred_info_module(CPredInfo, PredModule) },
-		{ pred_info_name(CPredInfo, PredName) },
-		{ AditiPredProcId = proc(AditiPredId, AditiProcId) },
-		{ CallGoal = call(AditiPredId, AditiProcId, HeadVars,
-			not_builtin, no, qualified(PredModule, PredName))
-				- CallGoalInfo },
-
-		{ JoinProcInfo0 = CProcInfo },
-		{ proc_info_set_headvars(JoinProcInfo0,
-			HeadVars, JoinProcInfo1) },
-		{ proc_info_set_argmodes(JoinProcInfo1,
-			ArgModes, JoinProcInfo2) },
-
-		% Imported procedures such as base relations have nothing
-		% in their vartypes field, so create it here.
-		{ map__from_corresponding_lists(HeadVars0,
-			ArgTypes, VarTypes) },
-		{ proc_info_set_vartypes(JoinProcInfo2,
-			VarTypes, JoinProcInfo3) },
-		{ proc_info_set_goal(JoinProcInfo3, CallGoal, JoinProcInfo) },
-		magic__build_join_pred_info(CPredProcId, CPredInfo,
-			JoinProcInfo, HeadVars, JoinPredProcId, _JoinPredInfo1)
+		pred_proc_id::out, module_info::in, module_info::out) is det.
+
+magic__create_input_join_proc(CPredProcId, AditiPredProcId, JoinPredProcId,
+		ModuleInfo0, ModuleInfo) :-
+	module_info_pred_proc_info(ModuleInfo0, CPredProcId,
+		CPredInfo, CProcInfo),
+	proc_info_argmodes(CProcInfo, ArgModes0),
+	pred_info_arg_types(CPredInfo, ArgTypes),
+	type_util__remove_aditi_state(ArgTypes, ArgModes0, ArgModes),
+	partition_args(ModuleInfo0, ArgModes, ArgModes,
+		InputArgModes, OutputArgModes),
+
+	%
+	% The interface procedure on the Aditi side must have
+	% only one input closure argument.
+	%
+	proc_info_vartypes(CProcInfo, VarTypes0),
+	proc_info_headvars(CProcInfo, HeadVars0),
+	type_util__remove_aditi_state(ArgTypes,
+		HeadVars0, HeadVars),
+
+	partition_args(ModuleInfo0, ArgModes, HeadVars,
+		InputArgs, OutputArgs),
+
+	map__apply_to_list(InputArgs, VarTypes0, InputVarTypes),
+
+	construct_higher_order_type(predicate, (aditi_bottom_up),
+		InputVarTypes, ClosureVarType),
+	list__map(magic_util__mode_to_output_mode(ModuleInfo0),
+		InputArgModes, MagicArgModes),
+
+	JoinProcInfo0 = CProcInfo,
+	proc_info_create_var_from_type(JoinProcInfo0,
+		ClosureVarType, ClosureVar, JoinProcInfo1),
+
+
+	%
+	% Build a goal to call the input closure.
+	%
+
+	set__list_to_set([ClosureVar | InputArgs],
+		HOCallNonLocals),
+	instmap_delta_from_mode_list(InputArgs, MagicArgModes,
+		ModuleInfo0, HOCallDelta),
+	goal_info_init(HOCallNonLocals, HOCallDelta, nondet,
+		InputGoalInfo),
+	list__length(InputArgs, Arity),
+	InputGoal = generic_call(
+		higher_order(ClosureVar, predicate, Arity),
+		InputArgs, MagicArgModes, nondet) - InputGoalInfo,
+
+	ClosureInst = ground(shared,
+	    yes(pred_inst_info(predicate, MagicArgModes, nondet))),
+	ClosureMode = (ClosureInst -> ClosureInst),
+	proc_info_set_argmodes(JoinProcInfo1,
+		[ClosureMode | OutputArgModes], JoinProcInfo2),
+	proc_info_set_headvars(JoinProcInfo2,
+		[ClosureVar | OutputArgs], JoinProcInfo3),
+
+	magic__build_join_pred_info(CPredProcId, CPredInfo,
+		JoinProcInfo3, [ClosureVar | OutputArgs],
+		JoinPredProcId, JoinPredInfo, ModuleInfo0, ModuleInfo1),
+
+	%
+	% Build a call to the Aditi procedure.
+	%
+
+	AditiPredProcId = proc(AditiPredId, AditiProcId),
+	proc_info_goal(CProcInfo, _ - CallGoalInfo0),
+
+	% Convert input arguments to output arguments, producing
+	% the tests which will make up the join condition.
+	magic_util__create_input_test_unifications(ModuleInfo1, HeadVars,
+		InputArgs, ArgModes, CallArgs0, [], Tests,
+		CallGoalInfo0, CallGoalInfo, JoinProcInfo3, JoinProcInfo4),
+
+	( hlds_pred__pred_info_is_base_relation(CPredInfo) ->
+		CallArgs = CallArgs0
 	;
-		% The interface procedure on the Aditi side must have
-		% only one input closure argument.
-		{ proc_info_vartypes(CProcInfo, VarTypes0) },
-		{ proc_info_headvars(CProcInfo, HeadVars0) },
-		{ type_util__remove_aditi_state(ArgTypes,
-			HeadVars0, HeadVars) },
-
-		{ partition_args(ModuleInfo0, ArgModes, HeadVars,
-			InputArgs, OutputArgs) },
-
-		{ map__apply_to_list(InputArgs, VarTypes0, InputVarTypes) },
-
-		{ construct_higher_order_type(predicate, (aditi_bottom_up),
-			InputVarTypes, ClosureVarType) },
-		{ list__map(magic_util__mode_to_output_mode(ModuleInfo0),
-			InputArgModes, MagicArgModes) },
-	
-		{ JoinProcInfo0 = CProcInfo },
-		{ proc_info_create_var_from_type(JoinProcInfo0,
-			ClosureVarType, ClosureVar, JoinProcInfo1) },
-
-		% Build a goal to call the input closure.
-		{ set__list_to_set([ClosureVar | InputArgs],
-			HOCallNonLocals) },
-		{ instmap_delta_from_mode_list(InputArgs, MagicArgModes,
-			ModuleInfo0, HOCallDelta) },
-		{ goal_info_init(HOCallNonLocals, HOCallDelta, nondet,
-			InputGoalInfo) },
-		{ list__length(InputArgs, Arity) },
-		{ InputGoal = generic_call(
-			higher_order(ClosureVar, predicate, Arity),
-			InputArgs, MagicArgModes, nondet) - InputGoalInfo },
-
-		% Build a call to the original C proc.
-		{ CPredProcId = proc(CPredId, CProcId) },
-		{ proc_info_goal(CProcInfo, _ - CallGoalInfo) },
-		{ pred_info_module(CPredInfo, PredModule) },
-		{ pred_info_name(CPredInfo, PredName) },
-		{ CallGoal = call(CPredId, CProcId, HeadVars0, not_builtin,
-			no, qualified(PredModule, PredName)) - CallGoalInfo },
-		magic_info_get_magic_map(MagicMap),
-		(
-			{ magic_util__goal_is_aditi_call(ModuleInfo0,
-				MagicMap, CallGoal, DBCall0, _) }
-		->
-			{ DBCall = DBCall0 }	
-		;
-			{ error("magic__create_input_join_proc: not db_call") }
-		),
-
-		{ ClosureInst = ground(shared,
-		    yes(pred_inst_info(predicate, MagicArgModes, nondet))) },
-		{ ClosureMode = (ClosureInst -> ClosureInst) },
-		{ proc_info_set_argmodes(JoinProcInfo1,
-			[ClosureMode | OutputArgModes], JoinProcInfo2) },
-		{ proc_info_set_headvars(JoinProcInfo2,
-			[ClosureVar | OutputArgs], JoinProcInfo3) },
-
-		magic__build_join_pred_info(CPredProcId, CPredInfo,
-			JoinProcInfo3, [ClosureVar | OutputArgs],
-			JoinPredProcId, JoinPredInfo1),
-
-		% Transform the new goal.
-		magic_info_set_magic_vars([ClosureVar]),
-		% We don't want the call to be treated as recursive.
-		magic_info_set_scc([]),	
-		magic_info_set_pred_info(JoinPredInfo1),
-		magic_info_set_proc_info(JoinProcInfo3),
-
-		% Use the ho-call goal as input to the C proc, transforming
-		% the call to the C proc into a call to the corresponding
-		% Aditi proc.
-		{ set__list_to_set([ClosureVar | HeadVars], CallNonLocals) },
-		magic_util__setup_call([InputGoal], DBCall, 
-			CallNonLocals, Goals),
-
-		magic_info_get_module_info(ModuleInfo10),
-		{ instmap_delta_from_mode_list(OutputArgs, OutputArgModes,
-			ModuleInfo10, GoalDelta) },
-		{ set__list_to_set([ClosureVar | OutputArgs],
-			GoalNonLocals) },
-		{ goal_info_init(GoalNonLocals, GoalDelta, nondet, GoalInfo) },
-		{ conj_list_to_goal(Goals, GoalInfo, Goal) },
-		magic_info_get_proc_info(JoinProcInfo10),
-		{ proc_info_set_goal(JoinProcInfo10, Goal, JoinProcInfo) },
-		magic_info_get_pred_info(JoinPredInfo),
-		{ module_info_set_pred_proc_info(ModuleInfo10, JoinPredProcId,
-			JoinPredInfo, JoinProcInfo, ModuleInfo) },
-		magic_info_set_module_info(ModuleInfo)
-	).
+		CallArgs = [ClosureVar | CallArgs0]
+	),
+
+	module_info_pred_info(ModuleInfo1, AditiPredId, AditiPredInfo),
+	pred_info_module(AditiPredInfo, PredModule),
+	pred_info_name(AditiPredInfo, PredName),
+	CallGoal = call(AditiPredId, AditiProcId, CallArgs, not_builtin,
+		no, qualified(PredModule, PredName)) - CallGoalInfo,
+
+	instmap_delta_from_mode_list(OutputArgs, OutputArgModes,
+		ModuleInfo1, GoalDelta),
+	set__list_to_set([ClosureVar | OutputArgs],
+		GoalNonLocals),
+	goal_info_init(GoalNonLocals, GoalDelta, nondet, GoalInfo),
+	conj_list_to_goal([InputGoal, CallGoal | Tests], GoalInfo,
+		JoinGoal),
+	proc_info_set_goal(JoinProcInfo4, JoinGoal, JoinProcInfo),
+	module_info_set_pred_proc_info(ModuleInfo1, JoinPredProcId,
+		JoinPredInfo, JoinProcInfo, ModuleInfo).
 
 :- pred magic__build_join_pred_info(pred_proc_id::in, pred_info::in,
 		proc_info::in, list(prog_var)::in, pred_proc_id::out,
-		pred_info::out, magic_info::in, magic_info::out) is det.
+		pred_info::out, module_info::in, module_info::out) is det.
 
 magic__build_join_pred_info(CPredProcId, CPredInfo, JoinProcInfo,
-		Args, JoinPredProcId, JoinPredInfo1) -->
-	{ proc_info_vartypes(JoinProcInfo, JoinVarTypes) },
-	{ map__apply_to_list(Args, JoinVarTypes,
-		NewArgTypes) },
-	{ pred_info_module(CPredInfo, PredModule) },
-	{ CPredProcId = proc(_, CProcId) },
-	magic_util__make_pred_name(CPredInfo, CProcId,
-		"Aditi_C_Interface_Proc_For", no, NewPredName),
-	{ init_markers(Markers0) },
-	{ add_marker(Markers0, aditi, Markers1) },
-	{ add_marker(Markers1, aditi_no_memo, Markers2) },
-	{ add_marker(Markers2, naive, Markers) },
-	{ ClassContext = constraints([], []) },
-	{ pred_info_get_aditi_owner(CPredInfo, User) },
-	{ varset__init(TVarSet) },	% must be empty.
-	{ term__context_init(DummyContext) },
-	{ ExistQVars = [] },
-	{ set__init(Assertions) },
-	{ pred_info_create(PredModule, NewPredName,
+		Args, JoinPredProcId, JoinPredInfo1,
+		ModuleInfo0, ModuleInfo) :-
+	proc_info_vartypes(JoinProcInfo, JoinVarTypes),
+	map__apply_to_list(Args, JoinVarTypes, NewArgTypes),
+	pred_info_module(CPredInfo, PredModule),
+	rl__get_c_interface_proc_name(ModuleInfo0, CPredProcId, NewPredName),
+	init_markers(Markers0),
+	add_marker(Markers0, aditi, Markers1),
+	add_marker(Markers1, aditi_no_memo, Markers2),
+	add_marker(Markers2, naive, Markers),
+	ClassContext = constraints([], []),
+	pred_info_get_aditi_owner(CPredInfo, User),
+	varset__init(TVarSet),	% must be empty.
+	term__context_init(DummyContext),
+	ExistQVars = [],
+	set__init(Assertions),
+	pred_info_create(PredModule, qualified(PredModule, NewPredName),
 		TVarSet, ExistQVars, NewArgTypes, true, DummyContext,
 		exported, Markers, predicate, ClassContext, User, Assertions,
-		JoinProcInfo, JoinProcId, JoinPredInfo1) },
+		JoinProcInfo, JoinProcId, JoinPredInfo1),
 
-	magic_info_get_module_info(ModuleInfo0),
-	{ module_info_get_predicate_table(ModuleInfo0, Preds0) },
-	{ predicate_table_insert(Preds0, JoinPredInfo1,
-		JoinPredId, Preds) },
-	{ JoinPredProcId = proc(JoinPredId, JoinProcId) },
-	{ module_info_set_predicate_table(ModuleInfo0,
-		Preds, ModuleInfo) },
-	magic_info_set_module_info(ModuleInfo).
+	module_info_get_predicate_table(ModuleInfo0, Preds0),
+	predicate_table_insert(Preds0, JoinPredInfo1, JoinPredId, Preds),
+	JoinPredProcId = proc(JoinPredId, JoinProcId),
+	module_info_set_predicate_table(ModuleInfo0, Preds, ModuleInfo).
 
 	% The new procedure consists of a `aditi_call' goal,
 	% which call_gen.m generates as a call to do_*_aditi_call in
Index: compiler/magic_util.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/magic_util.m,v
retrieving revision 1.5
diff -u -u -r1.5 magic_util.m
--- compiler/magic_util.m	2000/02/21 00:05:02	1.5
+++ compiler/magic_util.m	2000/04/11 05:23:48
@@ -88,11 +88,11 @@
 	% Convert all modes to output, creating test unifications 
 	% where the original mode was input. This will result in
 	% a join on the input attributes.
-:- pred magic_util__create_input_test_unifications(list(prog_var)::in,
-		list(prog_var)::in, list(mode)::in, list(prog_var)::out,
-		list(hlds_goal)::in, list(hlds_goal)::out,
+:- pred magic_util__create_input_test_unifications(module_info::in,
+		list(prog_var)::in, list(prog_var)::in, list(mode)::in,
+		list(prog_var)::out, list(hlds_goal)::in, list(hlds_goal)::out,
 		hlds_goal_info::in, hlds_goal_info::out,
-		magic_info::in, magic_info::out) is det.
+		proc_info::in, proc_info::out) is det.
 		
 	% Convert an input mode to output.
 :- pred magic_util__mode_to_output_mode(module_info::in,
@@ -372,14 +372,17 @@
 			% the input matches the output.
 			magic_info_get_module_info(ModuleInfo),
 			{ module_info_pred_proc_info(ModuleInfo, PredProcId,
-				PredInfo, ProcInfo) },
-			{ pred_info_module(PredInfo, PredModule) },
-			{ pred_info_name(PredInfo, PredName) },
+				CalledPredInfo, CalledProcInfo) },
+			{ pred_info_module(CalledPredInfo, PredModule) },
+			{ pred_info_name(CalledPredInfo, PredName) },
 			{ Name = qualified(PredModule, PredName) },
-			{ proc_info_argmodes(ProcInfo, ArgModes) },
-			magic_util__create_input_test_unifications(Args,
-				InputArgs, ArgModes, NewArgs, [], Tests,
-				CallGoalInfo0, CallGoalInfo1),
+			{ proc_info_argmodes(CalledProcInfo, ArgModes) },
+			magic_info_get_proc_info(ProcInfo0),
+			{ magic_util__create_input_test_unifications(
+				ModuleInfo, Args, InputArgs, ArgModes,
+				NewArgs, [], Tests, CallGoalInfo0,
+				CallGoalInfo1, ProcInfo0, ProcInfo) },
+			magic_info_set_proc_info(ProcInfo),
 			{ goal_info_get_nonlocals(CallGoalInfo1,
 				CallNonLocals1) },
 			magic_util__restrict_nonlocals(CallNonLocals1,
@@ -388,7 +391,7 @@
 				CallGoalInfo) },
 			{ CallGoal = call(PredId, ProcId, NewArgs,
 				not_builtin, no, Name) - CallGoalInfo }
-		;	
+		;
 			% Transform away the input arguments. 
 			magic_util__handle_input_args(PredProcId0, PredProcId,
 				PrevGoals, NonLocals, Args, InputArgs,
@@ -516,8 +519,12 @@
 
 	% Convert input args to outputs, and test that
 	% the input matches the output.
-	magic_util__create_input_test_unifications(Args, InputArgs,
-		OldArgModes, NewOutputArgs, [], Tests, GoalInfo0, GoalInfo1),
+	magic_info_get_module_info(ModuleInfo1),
+	magic_info_get_proc_info(ProcInfo0),
+	{ magic_util__create_input_test_unifications(ModuleInfo1,
+		Args, InputArgs, OldArgModes, NewOutputArgs, [], Tests,
+		GoalInfo0, GoalInfo1, ProcInfo0, ProcInfo) },
+	magic_info_set_proc_info(ProcInfo),
 
 	% All database predicates are considered nondet after this.
 	{ goal_info_set_determinism(GoalInfo1, 
@@ -549,76 +556,79 @@
 	{ CallGoal = call(PredId, ProcId, AllArgs, not_builtin, no,
 			qualified(PredModule, PredName)) - GoalInfo }.
 
-magic_util__create_input_test_unifications([], _, [_|_], _, _, _, _, _) --> 
-	{ error("magic_util__create_input_test_unifications") }.
-magic_util__create_input_test_unifications([_|_], _, [], _, _, _, _, _) -->
-	{ error("magic_util__create_input_test_unifications") }.
-magic_util__create_input_test_unifications([], _, [], [], Tests, Tests,
-		CallInfo, CallInfo) --> [].
-magic_util__create_input_test_unifications([Var | Vars], InputArgs,
+magic_util__create_input_test_unifications(_, [], _, [_|_],
+		_, _, _, _, _, _, _) :-
+	error("magic_util__create_input_test_unifications").
+magic_util__create_input_test_unifications(_, [_|_], _, [],
+		_, _, _, _, _, _, _) :-
+	error("magic_util__create_input_test_unifications").
+magic_util__create_input_test_unifications(_, [], _, [], [], Tests, Tests,
+		CallInfo, CallInfo, ProcInfo, ProcInfo).
+magic_util__create_input_test_unifications(ModuleInfo, [Var | Vars], InputArgs,
 		[Mode | Modes], [OutputVar | OutputVars], Tests0, Tests,
-		CallInfo0, CallInfo) -->
-	( { list__member(Var, InputArgs) } ->
-		magic_util__create_input_test_unification(Var, Mode,
-			OutputVar, Test, CallInfo0, CallInfo1),
-		{ Tests1 = [Test | Tests0] }
-	;
-		{ OutputVar = Var },
-		{ CallInfo1 = CallInfo0 },
-		{ Tests1 = Tests0 }
+		CallInfo0, CallInfo, ProcInfo0, ProcInfo) :-
+	( list__member(Var, InputArgs) ->
+		magic_util__create_input_test_unification(ModuleInfo,
+			Var, Mode, OutputVar, Test, CallInfo0, CallInfo1,
+			ProcInfo0, ProcInfo1),
+		Tests1 = [Test | Tests0]
+	;
+		ProcInfo1 = ProcInfo0,
+		OutputVar = Var,
+		CallInfo1 = CallInfo0,
+		Tests1 = Tests0
 	),	
-	magic_util__create_input_test_unifications(Vars, InputArgs, Modes, 
-		OutputVars, Tests1, Tests, CallInfo1, CallInfo).
-
-:- pred magic_util__create_input_test_unification(prog_var::in, (mode)::in,
-		prog_var::out, hlds_goal::out, hlds_goal_info::in,
-		hlds_goal_info::out, magic_info::in, magic_info::out) is det.
-
-magic_util__create_input_test_unification(Var, Mode, OutputVar, Test,
-		CallInfo0, CallInfo) -->
-	magic_info_get_module_info(ModuleInfo0),
-	{ mode_get_insts(ModuleInfo0, Mode, _, FinalInst) }, 
-	magic_info_get_proc_info(ProcInfo0),
-	{ proc_info_varset(ProcInfo0, VarSet0) },
-	{ varset__new_var(VarSet0, OutputVar, VarSet) },
-	{ proc_info_vartypes(ProcInfo0, VarTypes0) },
-	{ map__lookup(VarTypes0, Var, VarType) },
-	{ map__det_insert(VarTypes0, OutputVar, VarType, VarTypes) },
-	{ proc_info_set_varset(ProcInfo0, VarSet, ProcInfo1) },
-	{ proc_info_set_vartypes(ProcInfo1, VarTypes, ProcInfo) },
-	magic_info_set_proc_info(ProcInfo),
-
-	{ set__list_to_set([Var, OutputVar], NonLocals) },
-	{ instmap_delta_init_reachable(InstMapDelta) },
-	{ goal_info_init(NonLocals, InstMapDelta, semidet, GoalInfo) },
-	( { type_is_atomic(VarType, ModuleInfo0) } ->
+	magic_util__create_input_test_unifications(ModuleInfo, Vars, InputArgs,
+		Modes, OutputVars, Tests1, Tests, CallInfo1, CallInfo,
+		ProcInfo1, ProcInfo).
+
+:- pred magic_util__create_input_test_unification(module_info::in,
+		prog_var::in, (mode)::in, prog_var::out,
+		hlds_goal::out, hlds_goal_info::in,
+		hlds_goal_info::out, proc_info::in, proc_info::out) is det.
+
+magic_util__create_input_test_unification(ModuleInfo, Var, Mode, OutputVar,
+		Test, CallInfo0, CallInfo, ProcInfo0, ProcInfo) :-
+	mode_get_insts(ModuleInfo, Mode, _, FinalInst), 
+	proc_info_varset(ProcInfo0, VarSet0),
+	varset__new_var(VarSet0, OutputVar, VarSet),
+	proc_info_vartypes(ProcInfo0, VarTypes0),
+	map__lookup(VarTypes0, Var, VarType),
+	map__det_insert(VarTypes0, OutputVar, VarType, VarTypes),
+	proc_info_set_varset(ProcInfo0, VarSet, ProcInfo1),
+	proc_info_set_vartypes(ProcInfo1, VarTypes, ProcInfo),
+
+	set__list_to_set([Var, OutputVar], NonLocals),
+	instmap_delta_init_reachable(InstMapDelta),
+	goal_info_init(NonLocals, InstMapDelta, semidet, GoalInfo),
+	( type_is_atomic(VarType, ModuleInfo) ->
 		%
 		% The type is a builtin, so create a simple_test unification.
 		%
-		{ Unification = simple_test(Var, OutputVar) },
-		{ UnifyMode = ((FinalInst -> FinalInst) 
-			- (FinalInst -> FinalInst)) },
-		{ Test = unify(Var, var(OutputVar), UnifyMode,
-			Unification, unify_context(explicit, [])) - GoalInfo }
-	; { type_to_type_id(VarType, _TypeId, _ArgTypes) } ->
+		Unification = simple_test(Var, OutputVar),
+		UnifyMode = ((FinalInst -> FinalInst) 
+			- (FinalInst -> FinalInst)),
+		Test = unify(Var, var(OutputVar), UnifyMode,
+			Unification, unify_context(explicit, [])) - GoalInfo
+	; type_to_type_id(VarType, _TypeId, _ArgTypes) ->
 		% XXX for now we pretend that the unification is
 		% a simple test, since otherwise we would have to
 		% go through the rigmarole of creating type_info variables
 		% (and then ignoring them in code generation).
-		{ Unification = simple_test(Var, OutputVar) },
-		{ UnifyMode = ((FinalInst -> FinalInst) 
-			- (FinalInst -> FinalInst)) },
-		{ Test = unify(Var, var(OutputVar), UnifyMode,
-			Unification, unify_context(explicit, [])) - GoalInfo }
+		Unification = simple_test(Var, OutputVar),
+		UnifyMode = ((FinalInst -> FinalInst) 
+			- (FinalInst -> FinalInst)),
+		Test = unify(Var, var(OutputVar), UnifyMode,
+			Unification, unify_context(explicit, [])) - GoalInfo
 
 		/*
 		% 
 		% The type is non-builtin, so look up the unification 
 		% procedure for the type.
 		%
-		{ module_info_get_special_pred_map(ModuleInfo0,
-			SpecialPredMap) },
-		{ map__lookup(SpecialPredMap, unify - TypeId, UniPredId) },
+		module_info_get_special_pred_map(ModuleInfo,
+			SpecialPredMap),
+		map__lookup(SpecialPredMap, unify - TypeId, UniPredId),
 
 		% It had better be an in-in unification, since Aditi
 		% relations cannot have non-ground arguments. This is 
@@ -626,23 +636,23 @@
 		% XXX __Unify__/2 needs to be special cased in rl_exprn.m 
 		% because we don't add the type_info arguments.
 
-		{ hlds_pred__in_in_unification_proc_id(UniProcId) },
-		{ SymName = unqualified("__Unify__") },
-		{ ArgVars = [Var, OutputVar] },
-		{ Test = call(UniPredId, UniProcId, ArgVars, not_builtin,
-			no, SymName) - GoalInfo }
+		hlds_pred__in_in_unification_proc_id(UniProcId),
+		SymName = unqualified("__Unify__"),
+		ArgVars = [Var, OutputVar],
+		Test = call(UniPredId, UniProcId, ArgVars, not_builtin,
+			no, SymName) - GoalInfo
 		*/
 	;
-		{ error("magic_util__create_input_test_unifications: \
-			type_to_type_id failed") }
+		error("magic_util__create_input_test_unifications: \
+			type_to_type_id failed")
 	),
-	{ goal_info_get_nonlocals(CallInfo0, CallNonLocals0) },
-	{ set__delete(CallNonLocals0, Var, CallNonLocals1) },
-	{ set__insert(CallNonLocals1, OutputVar, CallNonLocals) },
-	{ goal_info_get_instmap_delta(CallInfo0, CallDelta0) },
-	{ instmap_delta_insert(CallDelta0, OutputVar, FinalInst, CallDelta) },
-	{ goal_info_set_nonlocals(CallInfo0, CallNonLocals, CallInfo1) },
-	{ goal_info_set_instmap_delta(CallInfo1, CallDelta, CallInfo) }.
+	goal_info_get_nonlocals(CallInfo0, CallNonLocals0),
+	set__delete(CallNonLocals0, Var, CallNonLocals1),
+	set__insert(CallNonLocals1, OutputVar, CallNonLocals),
+	goal_info_get_instmap_delta(CallInfo0, CallDelta0),
+	instmap_delta_insert(CallDelta0, OutputVar, FinalInst, CallDelta),
+	goal_info_set_nonlocals(CallInfo0, CallNonLocals, CallInfo1),
+	goal_info_set_instmap_delta(CallInfo1, CallDelta, CallInfo).
 
 %-----------------------------------------------------------------------------%
 
@@ -1127,8 +1137,15 @@
 		magic_info_get_curr_pred_proc_id(PredProcId),
 		magic_info_get_module_info(ModuleInfo),
 		( { type_is_aditi_state(ArgType) } ->
-			( { \+  mode_is_input(ModuleInfo, ArgMode) } ->
-				% aditi__states must be input
+			(
+				{ \+ mode_is_input(ModuleInfo, ArgMode) },
+
+				% The second `aditi__state' of the closure
+				% passed to `aditi_bulk_modify' has mode
+				% `unused'.
+				{ \+ mode_is_unused(ModuleInfo, ArgMode) }
+			->
+				% aditi__states must not be output.
 				{ StateError =
 					[argument_error(output_aditi_state,
 						ArgId, PredProcId) - Context] }
Index: compiler/make_hlds.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/make_hlds.m,v
retrieving revision 1.331
diff -u -u -r1.331 make_hlds.m
--- compiler/make_hlds.m	2000/04/11 07:57:00	1.331
+++ compiler/make_hlds.m	2000/04/12 06:53:11
@@ -588,10 +588,8 @@
 		io__set_exit_status(1),
 		{ module_info_incr_errors(Module0, Module) }
 	;
-		% There are local Aditi procedures - enable Aditi
-		% code generation.
-		{ module_info_set_do_aditi_compilation(Module0,
-			Module) }
+		% There are Aditi procedures - enable Aditi code generation.
+		{ module_info_set_do_aditi_compilation(Module0, Module) }
 	).
 
 %-----------------------------------------------------------------------------%
@@ -914,19 +912,9 @@
 		set__list_to_set(Args, NonLocals),
 		goal_info_set_nonlocals(GoalInfo0, NonLocals, GoalInfo1),
 		goal_info_set_context(GoalInfo1, Context, GoalInfo),
-		(
-			PredOrFunc = predicate,
-			invalid_proc_id(DummyProcId),
-			Goal = call(PredId, DummyProcId, Args,
-				not_builtin, no, SymName) - GoalInfo
-		;
-			PredOrFunc = function,
-			pred_args_to_func_args(Args, FuncArgs, RetArg),
-			ConsId = cons(SymName, Arity),
-			create_atomic_unification(RetArg,
-				functor(ConsId, FuncArgs), Context,
-				explicit, [], Goal)
-		),
+
+		construct_pred_or_func_call(PredId, PredOrFunc, SymName, Args,
+			GoalInfo, Goal),
 		Clause = clause(ProcIds, Goal, Context),
 		map__init(TI_VarMap),
 		map__init(TCI_VarMap),
@@ -5158,7 +5146,11 @@
 		; Name1 = "aditi_delete"
 		; Name1 = "aditi_bulk_insert"
 		; Name1 = "aditi_bulk_delete"
-		; Name1 = "aditi_modify"
+		; Name1 = "aditi_bulk_modify"
+
+		% These are not yet implemented in Aditi.
+		%; Name1 = "aditi_filter"
+		%; Name1 = "aditi_modify"
 		}
 	->
 		{ term__apply_substitution_to_list(Args0, Subst, Args1) },
@@ -5635,6 +5627,8 @@
 	;	"aditi_delete"
 	;	"aditi_bulk_insert"
 	;	"aditi_bulk_delete"
+	;	"aditi_filter"
+	;	"aditi_bulk_modify"
 	;	"aditi_modify"
 	).
 
@@ -5646,8 +5640,57 @@
 :- mode transform_aditi_builtin(in(aditi_update_str), in,
 		in, in, out, out, in, out, di, uo) is det.
 
-transform_aditi_builtin("aditi_insert", Args0, Context, VarSet0,
+transform_aditi_builtin(UpdateStr, Args0, Context, VarSet0,
 		Goal, VarSet, Info0, Info) -->
+	(
+		{ UpdateStr = "aditi_insert", InsertDelete = insert
+		; UpdateStr = "aditi_delete", InsertDelete = delete
+		}
+	->
+		transform_aditi_tuple_insert_delete(UpdateStr, InsertDelete,
+			Args0, Context, VarSet0, Goal,
+			VarSet, Info0, Info)
+	;
+		{
+			UpdateStr = "aditi_insert",
+			% This is handled above
+			error("transform_aditi_builtin: aditi_insert")
+		;
+			UpdateStr = "aditi_delete",
+			% This is handled above
+			error("transform_aditi_builtin: aditi_delete")
+		;
+			UpdateStr = "aditi_bulk_insert",
+			Update = bulk_insert
+		;
+			UpdateStr = "aditi_bulk_delete",
+			Update = delete(bulk)
+		;
+			UpdateStr = "aditi_bulk_modify",
+			Update = modify(bulk)
+		;
+			UpdateStr = "aditi_filter",
+			% not yet implemented
+			Update = delete(filter)
+		;
+			UpdateStr = "aditi_modify",
+			% not yet implemented
+			Update = modify(filter)
+		},
+		transform_aditi_insert_delete_modify(UpdateStr,
+			Update, Args0, Context, VarSet0, Goal,
+			VarSet, Info0, Info)
+	).
+
+:- pred transform_aditi_tuple_insert_delete(string, aditi_insert_delete,
+		list(prog_term), prog_context,
+		prog_varset, hlds_goal, prog_varset,
+		qual_info, qual_info, io__state, io__state).
+:- mode transform_aditi_tuple_insert_delete(in, in, in, in,
+		in, out, out, in, out, di, uo) is det.
+
+transform_aditi_tuple_insert_delete(UpdateStr, InsertDelete, Args0, Context,
+		VarSet0, Goal, VarSet, Info0, Info) -->
 	% Build an empty goal_info. 
 	{ goal_info_init(Context, GoalInfo) },
 
@@ -5681,7 +5724,8 @@
 			list__length(TupleArgVars, InsertArity),
 
 			invalid_pred_id(PredId),
-			Builtin = aditi_insert(PredId),
+			Builtin = aditi_tuple_insert_delete(InsertDelete,
+					PredId),
 			InsertCallId = PredOrFunc - SymName/InsertArity,
 			Call = generic_call(
 				aditi_builtin(Builtin, InsertCallId),
@@ -5698,64 +5742,51 @@
 				Context, call(CallId), no,
 				Goal0, VarSet3, Goal, VarSet, Info0, Info)
 		;
-			{ invalid_goal("aditi_insert",
-				Args0, GoalInfo, Goal, VarSet0, VarSet) },
+			{ invalid_goal(UpdateStr, Args0, GoalInfo,
+				Goal, VarSet0, VarSet) },
 			{ qual_info_set_found_syntax_error(yes, Info0, Info) },
 			io__set_exit_status(1),
 			prog_out__write_context(Context),
-			io__write_string(
-		"Error: expected tuple to insert in `aditi_insert'.\n")
+			io__write_string("Error: expected tuple to "),
+			io__write(InsertDelete),
+			io__write_string(" in `"),
+			io__write_string(UpdateStr),
+			io__write_string("'.\n")
 		)
 	;
-		{ invalid_goal("aditi_insert", Args0, GoalInfo,
+		{ invalid_goal(UpdateStr, Args0, GoalInfo,
 			Goal, VarSet0, VarSet) },
 		{ qual_info_set_found_syntax_error(yes, Info0, Info) },
 		{ list__length(Args0, Arity) },
-		aditi_update_arity_error(Context, "aditi_insert", Arity, [3])
+		aditi_update_arity_error(Context, UpdateStr, Arity, [3])
 	).
-transform_aditi_builtin("aditi_delete", Args0, Context, VarSet0,
-		Goal, VarSet, Info0, Info) -->
-	transform_delete_or_modify("aditi_delete", Args0, Context, VarSet0,
-		Goal, VarSet, Info0, Info).
-transform_aditi_builtin("aditi_bulk_insert", Args0, Context, VarSet0,
-		Goal, VarSet, Info0, Info) -->
-	transform_bulk_update("aditi_bulk_insert", insert, Args0, Context,
-		VarSet0, Goal, VarSet, Info0, Info).
-transform_aditi_builtin("aditi_bulk_delete", Args0, Context, VarSet0,
-		Goal, VarSet, Info0, Info) -->
-	transform_bulk_update("aditi_bulk_delete", delete, Args0, Context,
-		VarSet0, Goal, VarSet, Info0, Info).
-transform_aditi_builtin("aditi_modify", Args0, Context, VarSet0,
-		Goal, VarSet, Info0, Info) -->
-	transform_delete_or_modify("aditi_modify", Args0, Context, VarSet0,
-		Goal, VarSet, Info0, Info).
 
-:- inst aditi_del_or_mod_str = bound("aditi_delete"; "aditi_modify").
-
 	% Parse an `aditi_delete' or `aditi_modify' goal.
-:- pred transform_delete_or_modify(string, list(prog_term), prog_context,
-		prog_varset, hlds_goal, prog_varset,
-		qual_info, qual_info, io__state, io__state).
-:- mode transform_delete_or_modify(in(aditi_del_or_mod_str), in,
-		in, in, out, out, in, out, di, uo) is det.
+:- pred transform_aditi_insert_delete_modify(string,
+		aditi_insert_delete_modify, list(prog_term), prog_context,
+		prog_varset, hlds_goal, prog_varset, qual_info, qual_info,
+		io__state, io__state).
+:- mode transform_aditi_insert_delete_modify(in, in, in, in, in, out, out,
+		in, out, di, uo) is det.
 
-transform_delete_or_modify(DelOrMod, Args0, Context, VarSet0,
-		UpdateGoal, VarSet, Info0, Info) -->
+transform_aditi_insert_delete_modify(Descr, InsertDelMod, Args0, Context,
+		VarSet0, UpdateGoal, VarSet, Info0, Info) -->
 	{ goal_info_init(Context, GoalInfo) },
 	(
 		{ list__length(Args0, Arity) },
 		{ Arity \= 3 },
 		{ Arity \= 4 }
 	->
-		{ invalid_goal(DelOrMod, Args0, GoalInfo,
+		{ invalid_goal(Descr, Args0, GoalInfo,
 			UpdateGoal, VarSet0, VarSet) },
 		{ qual_info_set_found_syntax_error(yes, Info0, Info) },
-		aditi_update_arity_error(Context, DelOrMod, Arity, [3, 4])
+		aditi_update_arity_error(Context, Descr, Arity, [3, 4])
 	;
-
 		%
 		% First syntax -
-		%	aditi_delete((p(X, Y, DB0) :- X = 2), DB0, DB).
+		%	aditi_insert((p(X, Y, _DB0) :- X = 2, Y = 1), DB0, DB).
+		% or
+		%	aditi_delete((p(X, Y, _DB0) :- X = 2), DB0, DB).
 		% or
 		% 	aditi_modify((p(X0, Y0, _DB0) ==> p(X0, Y, _DB) :-
 		%		X0 < 100, Y = Y0 + 1), DB0, DB).
@@ -5763,12 +5794,17 @@
 		{ Args0 = [HOTerm, AditiState0Term, AditiStateTerm] },
 		{ parse_rule_term(Context, HOTerm, HeadTerm, GoalTerm1) },
 		{ 
-			DelOrMod = "aditi_delete",
+			InsertDelMod = bulk_insert,
 			parse_pred_or_func_and_args(HeadTerm,
 				PredOrFunc, SymName, HeadArgs1),
 			list__length(HeadArgs1, PredArity)
 		;
-			DelOrMod = "aditi_modify",
+			InsertDelMod = delete(_),
+			parse_pred_or_func_and_args(HeadTerm,
+				PredOrFunc, SymName, HeadArgs1),
+			list__length(HeadArgs1, PredArity)
+		;
+			InsertDelMod = modify(_),
 			HeadTerm = term__functor(term__atom("==>"),
 				[LeftHeadTerm, RightHeadTerm], _),
 			parse_pred_or_func_and_args(LeftHeadTerm,
@@ -5800,7 +5836,7 @@
 			PredGoal0, VarSet3, Info0, Info1),
 		{ ArgContext = head(PredOrFunc, PredArity) },
 		insert_arg_unifications(HeadArgs, HeadArgs1, Context,
-			ArgContext, no, PredGoal0, VarSet3, PredGoal, VarSet4,
+			ArgContext, no, PredGoal0, VarSet3, PredGoal1, VarSet4,
 			Info1, Info2),
 
 		% Quantification will reduce this down to
@@ -5809,38 +5845,16 @@
 		{ set__delete_list(LambdaGoalVars0,
 			HeadArgs, LambdaGoalVars1) },
 		{ set__to_sorted_list(LambdaGoalVars1, LambdaNonLocals) },
-
-		{ in_mode(InMode) },
-		{ out_mode(OutMode) },
-		{ invalid_pred_id(PredId) },
-		{
-			DelOrMod = "aditi_delete",
-			Builtin = aditi_delete(PredId, Syntax),
-
-			% Modes for the arguments of the input tuple.
-			list__duplicate(PredArity, InMode, Modes),
-
-			LambdaPredOrFunc = PredOrFunc
-		;
-			DelOrMod = "aditi_modify",
-			Builtin = aditi_modify(PredId, Syntax),
-
-			% Modes for the arguments corresponding to
-			% the input tuple.
-			list__duplicate(PredArity, InMode, InModes),
 
-			% Modes for the arguments corresponding to
-			% the output tuple.
-			list__duplicate(PredArity, OutMode, OutModes),
-
-			list__append(InModes, OutModes, Modes),
-
-			% For `aditi_modify' the higher-order argument
-			% is always a predicate.
-			LambdaPredOrFunc = predicate
-		},	
-
+		{ aditi_delete_insert_delete_modify_goal_info(InsertDelMod,
+			PredOrFunc, SymName, PredArity, HeadArgs,
+			LambdaPredOrFunc, EvalMethod, LambdaModes,
+			Detism, PredGoal1, PredGoal) },
 		{ ModifiedCallId = PredOrFunc - SymName/PredArity },
+
+		{ invalid_pred_id(PredId) },
+		{ Builtin = aditi_insert_delete_modify(InsertDelMod,
+				PredId, Syntax) },
 		{ MainContext =
 			call(generic_call(
 				aditi_builtin(Builtin, ModifiedCallId)),
@@ -5859,9 +5873,9 @@
 
 		% Build the lambda expression for the modification condition.
 		{ create_atomic_unification(LambdaVar,
-			lambda_goal(LambdaPredOrFunc, (aditi_top_down),
+			lambda_goal(LambdaPredOrFunc, EvalMethod,
 				FixModes, LambdaNonLocals,
-				HeadArgs, Modes, semidet, PredGoal),
+				HeadArgs, LambdaModes, Detism, PredGoal),
 			Context, MainContext, [], LambdaConstruct) },
 
 		{ make_fresh_arg_var(AditiState0Term, AditiState0Var, [],
@@ -5896,8 +5910,10 @@
 	;
 		%
 		% Second syntax -
-		% aditi_delete(p/3, (aditi_top_down pred(..) :- ..), DB0, DB).
-		% aditi_modify(p/3, (aditi_top_down pred(..) :- ..), DB0, DB).
+		% aditi_bulk_delete(pred p/3,
+		%	(aditi_bottom_up pred(..) :- ..), DB0, DB).
+		% aditi_bulk_modify(pred p/3,
+		%	(aditi_top_down pred(..) :- ..), DB0, DB).
 		%
 		% The `pred_term' syntax parsed above is transformed
 		% into the equivalent of this syntax.
@@ -5914,14 +5930,10 @@
 		{ make_fresh_arg_vars(OtherArgs0,
 			VarSet0, OtherArgs, VarSet1) },
 		{ invalid_pred_id(PredId) },
-		{
-			DelOrMod = "aditi_delete",
-			Builtin = aditi_delete(PredId, Syntax)
-		;
-			DelOrMod = "aditi_modify",
-			Builtin = aditi_modify(PredId, Syntax)
-		},	
-			
+
+		{ Builtin = aditi_insert_delete_modify(InsertDelMod,
+				PredId, Syntax) },
+
 		{ ModifiedCallId = PredOrFunc - SymName/Arity },
 		
 		% post_typecheck.m will fill this in.
@@ -5934,98 +5946,164 @@
 		insert_arg_unifications(OtherArgs, OtherArgs0, Context, CallId,
 			no, Call, VarSet1, UpdateGoal, VarSet, Info0, Info)
 	;
-		{ invalid_goal(DelOrMod, Args0, GoalInfo,
+		{ invalid_goal(Descr, Args0, GoalInfo,
 			UpdateGoal, VarSet0, VarSet) },
 		{ qual_info_set_found_syntax_error(yes, Info0, Info) },
 		io__set_exit_status(1),
-		(
-			{ DelOrMod = "aditi_delete" },
-			prog_out__write_context(Context),
-			io__write_string(
-	"Error: expected `aditi_delete((p(<Args>) :- <Goal>), DB0, DB)'\n"),
-			prog_out__write_context(Context),
-			io__write_string(
-	"  or `aditi_delete(PredOrFunc p/N, Closure, DB0, DB)'.\n")
-		;
-			{ DelOrMod = "aditi_modify" },
-			prog_out__write_context(Context),
-			io__write_string(
-		"Error: expected\n"),
-			prog_out__write_context(Context),
-			io__write_string(
-		"  `aditi_modify(\n"),
-			prog_out__write_context(Context),
-			io__write_string(
-		"    (p(<Args0>) ==> p(<Args>) :- <Goal>),\n"),
-			prog_out__write_context(Context),
-			io__write_string(
-		"    DB0, DB)'\n"),
-			prog_out__write_context(Context),
-			io__write_string(
-		"  or `aditi_modify(PredOrFunc p/N, Closure, DB0, DB)'.\n")
-		)
+		output_expected_aditi_update_syntax(Context, InsertDelMod)
 	).
 
-	% Parse an `aditi_bulk_insert' or `aditi_bulk_delete' goal.
-:- pred transform_bulk_update(string, aditi_bulk_operation, list(prog_term),
-		term__context, prog_varset, hlds_goal, prog_varset,
-		qual_info, qual_info, io__state, io__state).
-:- mode transform_bulk_update(in, in, in, in, in, out, out,
-		in, out, di, uo) is det.
+:- pred aditi_delete_insert_delete_modify_goal_info(aditi_insert_delete_modify,
+		pred_or_func, sym_name, arity, list(prog_var), pred_or_func,
+		lambda_eval_method, list(mode), determinism,
+		hlds_goal, hlds_goal).
+:- mode aditi_delete_insert_delete_modify_goal_info(in, in, in, in, in, out,
+		out, out, out, in, out) is det.
+
+aditi_delete_insert_delete_modify_goal_info(bulk_insert, PredOrFunc, _SymName,
+		PredArity, _Args, LambdaPredOrFunc, EvalMethod,
+		LambdaModes, Detism, Goal, Goal) :-
+	LambdaPredOrFunc = PredOrFunc,
+	EvalMethod = (aditi_bottom_up),
+	out_mode(OutMode),
+	Detism = nondet,
+	% Modes for the arguments of the input tuple.
+	list__duplicate(PredArity, OutMode, LambdaModes).
+
+aditi_delete_insert_delete_modify_goal_info(delete(BulkOrFilter), PredOrFunc, 
+		SymName, PredArity, Args, LambdaPredOrFunc, EvalMethod,
+		LambdaModes, Detism, Goal0, Goal) :-
+	LambdaPredOrFunc = PredOrFunc,
 
-transform_bulk_update(UpdateStr, BulkOp, Args0, Context, VarSet0, Goal, VarSet,
-		Info0, Info) -->
-	{ goal_info_init(Context, GoalInfo) },
 	(
-		{ Args0 = [PredCallIdTerm | OtherArgs0] },
-		% Higher-order term + threaded `aditi__state's
-		{ OtherArgs0 = [_, _, _] }
-	->
-		(
-			%
-			% Syntax -
-			% aditi_bulk_insert(p/3, Closure, DB0, DB).
-			% aditi_bulk_delete(p/3, Closure, DB0, DB).
-			%
-			{ parse_pred_or_func_name_and_arity(PredCallIdTerm,
-				PredOrFunc, SymName, Arity0) },
-			{ adjust_func_arity(PredOrFunc, Arity0, Arity) }
-		->
-			{ make_fresh_arg_vars(OtherArgs0, VarSet0,
-				OtherArgs, VarSet1) },
-			{ invalid_pred_id(PredId) },
-			{ Builtin = aditi_bulk_operation(BulkOp, PredId) },
-			{ ModifiedCallId = PredOrFunc - SymName/Arity },
-
-			% post_typecheck.m will fill this in.
-			{ GenericCallModes = [] },
-
-			{ Call = generic_call(
-				aditi_builtin(Builtin, ModifiedCallId),
-				OtherArgs, GenericCallModes, det) - GoalInfo },
-			insert_arg_unifications(OtherArgs, OtherArgs0, Context,
-				call(generic_call(
-				    aditi_builtin(Builtin, ModifiedCallId))),
-				no, Call, VarSet1, Goal, VarSet, Info0, Info)
-		;	
-			{ invalid_goal(UpdateStr,
-				Args0, GoalInfo, Goal, VarSet0, VarSet) },
-			{ qual_info_set_found_syntax_error(yes, Info0, Info) },
-			io__set_exit_status(1),
-			prog_out__write_context(Context),
-			io__write_string(
-		"Error: expected `PredOrFunc Name/Arity' in `"),	
-			io__write_string(UpdateStr),
-			io__write_string("'.\n")
-		)
+		BulkOrFilter = filter,
+		EvalMethod = (aditi_top_down),
+		in_mode(InMode),
+		list__duplicate(PredArity, InMode, LambdaModes),
+		Detism = semidet,
+		Goal = Goal0
 	;
-		{ invalid_goal(UpdateStr, Args0, GoalInfo, Goal,
-			VarSet0, VarSet) },
-		{ qual_info_set_found_syntax_error(yes, Info0, Info) },
-		{ list__length(Args0, Arity) },
-		aditi_update_arity_error(Context, UpdateStr, Arity, [4])
-	).	
+		BulkOrFilter = bulk,
+		EvalMethod = (aditi_bottom_up),
+		Detism = nondet,
+		out_mode(OutMode),
+		list__duplicate(PredArity, OutMode, LambdaModes),
+
+		% Join the result of the deletion goal with the
+		% relation to be updated.
+		conjoin_aditi_update_goal_with_call(PredOrFunc, SymName,
+			Args, Goal0, Goal)
+	).
+
+aditi_delete_insert_delete_modify_goal_info(modify(BulkOrFilter), PredOrFunc, 
+		SymName, PredArity, Args, LambdaPredOrFunc, EvalMethod,
+		LambdaModes, Detism, Goal0, Goal) :-
+
+	% The closure passed to `aditi_modify' and `aditi_bulk_modify'
+	% is always a predicate closure.
+	LambdaPredOrFunc = predicate,
+
+	in_mode(InMode),
+	out_mode(OutMode),
+	(
+		BulkOrFilter = filter,
 
+		% Modes for the arguments corresponding to
+		% the input tuple.
+		list__duplicate(PredArity, InMode,
+			DeleteModes),
+		EvalMethod = (aditi_top_down),
+		Detism = semidet,
+		Goal = Goal0
+	;
+		BulkOrFilter = bulk,
+		EvalMethod = (aditi_bottom_up),
+		Detism = nondet,
+
+		% Modes for the arguments corresponding to
+		% the input tuple.
+		list__duplicate(PredArity, OutMode,
+			DeleteModes),
+
+		% `Args' must have length `PredArity * 2',
+		% so this will always succeed.
+		( list__take(PredArity, Args, CallArgs0) ->
+			CallArgs = CallArgs0
+		;
+			error("aditi_delete_insert_delete_modify_goal_info")
+		),
+
+		% Join the result of the modify goal with the
+		% relation to be updated.
+		conjoin_aditi_update_goal_with_call(PredOrFunc, SymName,
+			CallArgs, Goal0, Goal)
+	),
+
+	% Modes for the arguments corresponding to
+	% the output tuple.
+	list__duplicate(PredArity, OutMode, InsertModes),
+	list__append(DeleteModes, InsertModes, LambdaModes).
+
+:- pred conjoin_aditi_update_goal_with_call(pred_or_func, sym_name,
+		list(prog_var), hlds_goal, hlds_goal).
+:- mode conjoin_aditi_update_goal_with_call(in, in, in, in, out) is det.
+
+conjoin_aditi_update_goal_with_call(PredOrFunc, SymName, Args, Goal0, Goal) :-
+	invalid_pred_id(PredId),
+	Goal0 = _ - GoalInfo,
+	construct_pred_or_func_call(PredId, PredOrFunc, SymName, Args,
+		GoalInfo, CallGoal),
+	Goal = conj([CallGoal, Goal0]) - GoalInfo.
+	
+:- pred output_expected_aditi_update_syntax(prog_context,
+		aditi_insert_delete_modify, io__state, io__state). 
+:- mode output_expected_aditi_update_syntax(in, in, di, uo) is det.
+
+output_expected_aditi_update_syntax(Context, bulk_insert) -->
+	output_insert_or_delete_expected_syntax(Context, "aditi_bulk_insert").
+output_expected_aditi_update_syntax(Context, delete(bulk)) -->
+	output_insert_or_delete_expected_syntax(Context, "aditi_bulk_delete").
+output_expected_aditi_update_syntax(Context, delete(filter)) -->
+	output_insert_or_delete_expected_syntax(Context, "aditi_delete").
+output_expected_aditi_update_syntax(Context, modify(BulkOrFilter)) -->
+	{ BulkOrFilter = bulk, Name = "aditi_bulk_modify"
+	; BulkOrFilter = filter, Name = "aditi_modify"
+	},
+	prog_out__write_context(Context),
+	io__write_string("Error: expected\n"),
+	prog_out__write_context(Context),
+	io__write_string("  `"),
+	io__write_string(Name),
+	io__write_string("(\n"),
+	prog_out__write_context(Context),
+	io__write_string(
+		"    (p(<Args0>) ==> p(<Args>) :- <Goal>),\n"),
+	prog_out__write_context(Context),
+	io__write_string(
+	"    DB0, DB)'\n"),
+	output_aditi_closure_syntax(Context, Name).
+
+:- pred output_insert_or_delete_expected_syntax(prog_context, string,
+		io__state, io__state).
+:- mode output_insert_or_delete_expected_syntax(in, in, di, uo) is det.
+
+output_insert_or_delete_expected_syntax(Context, Name) -->
+	prog_out__write_context(Context),
+	io__write_string("Error: expected `"),
+	io__write_string(Name),
+	io__write_string("((p(<Args>) :- <Goal>), DB0, DB)'\n"),
+	output_aditi_closure_syntax(Context, Name).
+
+:- pred output_aditi_closure_syntax(prog_context, string,
+		io__state, io__state).
+:- mode output_aditi_closure_syntax(in, in, di, uo) is det.
+
+output_aditi_closure_syntax(Context, Name) -->
+	prog_out__write_context(Context),
+	io__write_string("  or `"),
+	io__write_string(Name),
+	io__write_string("(PredOrFunc p/N, Closure, DB0, DB)'.\n").
+
 	% Report an error for an Aditi update with the wrong number
 	% of arguments.
 :- pred aditi_update_arity_error(prog_context, string, int, list(int),
@@ -6757,6 +6835,30 @@
 		lambda_goal(PredOrFunc, EvalMethod, modes_are_ok,
 			LambdaNonLocals, LambdaVars, Modes, Det, HLDS_Goal),
 		Context, MainContext, SubContext, Goal) }.
+
+%-----------------------------------------------------------------------------%
+
+:- pred construct_pred_or_func_call(pred_id, pred_or_func, sym_name,
+		list(prog_var), hlds_goal_info, hlds_goal).
+:- mode construct_pred_or_func_call(in, in, in, in, in, out) is det.
+
+construct_pred_or_func_call(PredId, PredOrFunc, SymName, Args, GoalInfo,
+		Goal) :-
+	(
+		PredOrFunc = predicate,
+		invalid_proc_id(DummyProcId),
+		Goal = call(PredId, DummyProcId, Args,
+			not_builtin, no, SymName) - GoalInfo
+	;
+		PredOrFunc = function,
+		pred_args_to_func_args(Args, FuncArgs, RetArg),
+		list__length(FuncArgs, Arity),
+		ConsId = cons(SymName, Arity),
+		goal_info_get_context(GoalInfo, Context),
+		create_atomic_unification(RetArg,
+			functor(ConsId, FuncArgs), Context,
+			explicit, [], Goal)
+	).
 
 %-----------------------------------------------------------------------------%
 
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------



More information about the developers mailing list