[m-dev.] for review: update state transformation
Peter Ross
petdr at cs.mu.OZ.AU
Thu Mar 30 11:49:09 AEST 2000
On 29-Mar-2000, Fergus Henderson <fjh at cs.mu.OZ.AU> wrote:
> On 29-Mar-2000, Peter Ross <petdr at cs.mu.OZ.AU> wrote:
>
> > library/int.m:
> > Add associativity declarations for + and *.
>
> Hmm... that might be a bit problematic in future,
> if/when we add (optional) range checking for ints.
>
> In particular, the accumulator transformation could then transform a program
> which does not overflow into one that does overflow.
>
> Well, I suppose we can deal with that issue if/when we add
> range checking. Just something to bear in mind...
>
> > Index: library/int.m
> ...
> > - % commutivity of +
> > + % commutivity and associativity of +
> > :- promise all [A,B,C] ( C = B + A <=> C = A + B ).
> > +:- promise all [A,B,C,ABC] ( ABC = (A + B) + C <=> ABC = A + (B + C) ).
> >
> > - % commutivity of *
> > + % commutivity and associativity of *
> > :- promise all [A,B,C] ( C = B * A <=> C = A * B ).
> > +:- promise all [A,B,C,ABC] ( ABC = (A * B) * C <=> ABC = A * (B * C) ).
>
> Is there some reason why they can't be written as just
>
> :- promise all [A,B,C] ( A * B = B * A ).
> :- promise all [A,B,C] ( (A * B) * C = A * (B * C) ).
>
> ?
>
> If so, I think it would be a good idea to document that reason.
>
There is no good reason apart from the fact that it isn't implemented.
I also believe it would be more difficult to match on the = form as you
no longer have the <=> to divide the LHS and RHS.
I will put it down on the TODO list though.
> > Index: library/list.m
> ...
> > + % construction equivalence law.
> > + % XXX when we implement rewrite rules change this law to a
> > + % rewrite rule.
> > +:- promise all [L,H,T] ( append([H], T, L) <=> L = [H|T] ).
>
> I think it would be a good idea to document why this rule is
> worth declaring.
>
How about
This construction equivalence law is useful for transforming predicates
into a tail recursive form, please see "Making Mercury Programs Tail
Recursive", available from http://www.cs.mu.oz.au/mercury, for more
information.
> > Index: compiler/accumulator.m
> ...
> > % Attempts to transform a single proc to a tail recursive form by
> > % introducing accumlators. The algorithm can do this if the code after
> > % the recursive call has either the state update or associative
> > % property.
> > %
> > % /* State update property */
> > % :- promise all [A,B,S0,S]
> > % (
> > % (some[SA] (update(A, S0, SA), update(B, SA, S)))
> > % <=>
> > % (some[SB] (update(B, S0, SB), update(A, SB, S)))
> > % ).
>
> Hmm, that name is not a great name for that property.
> There are many predicates which I would think of as
> "state update predicates", for example `io__set_exit_status',
> which do not have that property.
>
> Perhaps "Commutative state update property" would be a better name.
> It's not commutativity, but it is a bit like commutativity,
> as the following alternative way of expressing it shows:
>
> :- promise all [A, B]
> ((pred) --> update(A), update(B)) = ((pred) --> update(B), update(A)).
>
> Note that currently this is not legal Mercury syntax,
> because we require mode and determinism annotations on lambda expressions.
> This is necessary in goals, but it is not necessary in
> `:- promise' declarations (which do not need to be mode-correct),
> so it might be nice to relax that restriction so that
> it only applies to goals that need to be mode-correct.
> (As a side effect, this change would help the compiler
> give better error messages in the case where you forget
> these annotations.)
>
> Currently you would have to insert some dummy modes, e.g.
>
> :- promise all [A, B]
> (pred(in, in) is semidet --> update(A), update(B)) =
> (pred(in, in) is semidet --> update(B), update(A)).
>
I have changed it to `order independent state update property', which is
the name we use in the paper.
The reason why it is a bit like commutativity is that a predicate which
has the properties of commutativity and associativity implies the order
independent state update property.
However the converse assumption is a seductively dangerous fallacy (to
quote my Physical Electronics lecturer).
> > % The transformation recognises predicates in the form
> > %
> > % p(In, OutUpdate, OutAssoc) :-
> > % minimal(In),
> > % initialise(OutUpdate),
> > % base(OutAssoc).
> > % p(In, OutUpdate, OutAssoc) :-
> > % decompose(In, Current, Rest),
> > % p(Rest, OutUpdate0, OutAssoc0),
> > % update(Current, OutUpdate0, OutUpdate),
> > % assoc(Current, OutAssoc0, OutAssoc).
> > %
> > % which can be transformed by the algorithm in "State Update
> > % Transformation" to
> > %
> > % p(In, OutUpdate, OutAssoc) :-
> > % initialise(AccUpdate),
> > % p_acc(In, OutUpdate, OutAssoc, AccUpdate).
> > %
> > % p_acc(In, OutUpdate, OutAssoc, AccUpdate) :-
> > % minimal(In),
> > % base(OutAssoc),
> > % OutUpdate = AccUpdate.
> > % p_acc(In, OutUpdate, OutAssoc, AccUpdate0) :-
> > % decompose(In, Current, Rest),
> > % update(Current, AccUpdate0, AccUpdate),
> > % p_acc(Rest, OutUpdate, OutAssoc0, AccUpdate),
> > % compose(Current, OutAssoc0, OutAssoc).
>
> Shouldn't `compose' here be `assoc'?
> The first code fragment contains a call to `assoc',
> where the second code fragment contains a call to `compose'.
>
Yes, a cut and paste error.
> > % Note that if the recursive clause contains multiple calls to p, the
> > % transformation is attempts to move each recursive call to the end
>
> s/is attempts/attempts/
>
> > % A goal is represented by two integers, the first integer
> > % stores which conjunction the goal came from (base or
> > % recursive), and the second stores the location of the goal in
> > % the conjunction.
> > :- type goal_id == pair(int).
>
> How is the first int encoded? Why use an int rather than
> an enumeration type?
>
Hysterical raisons. I will change it to be a enumeration type.
> How is the second int encoded -- starting at zero or one?
>
It doesn't matter, as long as a goal that appears earlier in the body
has a lower number then one that appears later.
> > :- type warning
> > % warn that two prog_vars in call to pred_id
> > % at prog_context were swapped, which may cause
> > % an efficiency problem.
> > ---> w(prog_context, pred_id, prog_var, prog_var).
>
> I suggest s/w/warn/
>
> > :- type warnings == list(warning).
> >
> > %-----------------------------------------------------------------------------%
> >
> > %
> > % process_proc
> > %
> > % Attempt to transform one procedure into a accumulator
> > % recursive form.
>
> s/one/a/
> s/a //
>
> > process_proc(PredId, ProcId, ProcInfo0, ProcInfo,
> > ModuleInfo0, ModuleInfo) -->
> ...
> > globals__io_lookup_bool_option(inhibit_accumulator_warnings,
> > InhibitWarnings),
> > (
> > { not (InhibitWarnings = no, Warnings \= []) }
> > ->
>
> I think it would be clearer to avoid the double negation and write that
> condition as `InhibitWarnings = yes ; Warnings = []'
>
Done.
> (
> {
> ->
>
> > words("If a predicate has been"),
> > words("declared associative via"),
> > words("a promise declaration,"),
>
> I suggest s/promise/`promise'/
>
Done.
> > output_warning(w(Context, PredId, VarA, VarB), VarSet, ModuleInfo,
> > Context, Formats) :-
> > error_util__describe_one_pred_name(ModuleInfo, PredId, PredStr),
> > varset__lookup_name(VarSet, VarA, VarAStr),
> > varset__lookup_name(VarSet, VarB, VarBStr),
> > Formats = [words("warning: the call to"), words(PredStr),
> > words("has had the location of the variables"),
> > words(VarAStr), words("and"), words(VarBStr),
> > words("swapped to allow accumulator introduction.")
> > ].
>
> The predicate name and variable names should be inside single quotes, e.g.
> "`Foo'" rather than "Foo".
>
Done.
> > standardise(Goal0, Goal),
> >
> > identify_goal_type(PredId, ProcId, Goal, TopLevel, Base, Rec),
> >
> > C = initialise_goal_store(Rec, Base, InitialInstMap),
>
> I suggest s/standardise/standardize/g
> and s/initialise/initialize/g,
> for consistency with the spelling used in (most of)
> the rest of the compiler.
>
OK, but only under duress. ;)
> > %
> > % This predicate takes the original goal and identifies the
> > % `shape' of the goal around the recursive and base cases.
> > % Note that the base case can contain a recursive call, as the
> > % transformation doesn't depend on what is in the base case.
> > %
> > :- pred identify_goal_type(pred_id::in, proc_id::in, hlds_goal::in,
> > top_level::out, hlds_goals::out, hlds_goals::out) is semidet.
> >
> > identify_goal_type(PredId, ProcId, Goal, Type, Base, Rec) :-
> > (
> > Goal = switch(_Var, _CanFail, Cases, _StoreMap) - _GoalInfo,
> > Cases = [case(_IdA, GoalA), case(_IdB, GoalB)],
> > goal_to_conj_list(GoalA, GoalAList),
> > goal_to_conj_list(GoalB, GoalBList)
> > ->
> > (
> > is_recursive_case(GoalAList, proc(PredId, ProcId))
> > ->
> > Type = switch_rec_base,
> > Base = GoalBList,
> > Rec = GoalAList
> > ;
> > is_recursive_case(GoalBList, proc(PredId, ProcId))
> > ->
> > Type = switch_base_rec,
> > Base = GoalAList,
> > Rec = GoalBList
> > ;
> > fail
> > )
> > ;
> > fail
> > ).
>
> Hmm... what about the other possible values for `Type',
> e.g. when the goal is an if-then-else or a (semidet?) disjunction?
>
I just haven't ported those cases from my original code yet. It is just
a SMOP and will be implemented next.
> > :- type store_info
> > ---> store_info(
> > int,
> > instmap,
> > goal_store
> > ).
>
> What does this type represent?
> In particular, what does the `int' field represent?
>
How about.
% The store info is folded over the list of goals which
% represent the base and recursive case conjunctions.
:- type store_info
---> store_info(
int, % the location of the goal in
% the conjunction.
instmap,
goal_store
).
--------------------------------------------------------------------------
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