implementation of `freeze' (coroutining)

Fergus Henderson fjh at cs.mu.oz.au
Thu Sep 18 15:00:13 AEST 1997


Hi,

Here's an implementation of freeze/2 in Mercury,
and a coroutining Mercury version of N-queens that uses it.

Bugs: this implementation does not detect floundering.
(Not too hard to add, although it will add 

-----------------------------------------------------------------------------
:- module var.
:- interface.

:- type var(T).

	% `init(Var)' can be used to initialize
	% the inst of a variable to `any'.
:- pred init(var(T)::out(any)) is det.

	% `Var = var(Value)' unifies a variable with its value.
	% This can be used in several ways:
	% to bind a variable to a particular value, `X = var(42)';
	% to extract the value of that variable, `X = var(Y)';
	% or (NYI) to initialize the inst of a variable to any, `X = var(_)'.
:- func var(T) = var(T).
:- mode var(in) = out is det.
:- mode var(in) = in(any) is semidet.
:- mode var(in) = in is semidet.
:- mode var(out) = in is det.
% :- mode var(free -> clobbered_any) = out(any) is det. % NYI

	% `Var1 == Var2' can be used to unify two variables.
:- pred var(T) == var(T).
:- mode in == in is semidet.
:- mode in == out is det.
:- mode out == in is det.
:- mode in(any) == in(any) is semidet.
:- mode in(any) == out(any) is det.
:- mode out(any) == out(any) is det.
:- mode out(any) == in(any) is det.

	% `freeze(Var, Pred)' can be used to delay execution of a goal
	% until a variable is ground.
	% Often the freeze/3 version is more useful, though,
	% since this version doesn't allow `Pred' to have any outputs.
	% (XXX the compiler doesn't check that yet - this is a bug!)
	% Declaratively, freeze(Var, Pred) is true iff Pred(Var) is true.
	% Operationally, freeze(Var, Pred) delays until Var becomes ground
	% and then calls Pred(Var).
:- pred freeze(var(T), pred(T)).
:- mode freeze(in(any), pred(in) is semidet) is semidet.
:- mode freeze(out(any), pred(in) is semidet) is semidet.

/*****
NOT YET IMPLEMENTED -- the implementation requires existential types.
	% `freeze(Var1, Pred, Var2)' can be used to delay
	% execution of a goal until a variable is ground.
	% This version is more flexible than freeze/2, since
	% it allows the delayed goal to have an output.
	% Declaratively, freeze(X, Pred, Y) is true iff Pred(X, Y) is true.
	% Operationally, freeze(X, Pred, Y) delays until X becomes ground
	% and then calls Pred(X, Y).
:- pred freeze(var(T1), pred(T1, T2), var(T2)).
:- mode freeze(in(any), pred(in, out) is semidet, in(any)) is semidet.
:- mode freeze(out(any), pred(in, out) is det, out(any)) is semidet.

******/

%---------------------------------------------------------------------------%
:- implementation.
:- import_module list.
%---------------------------------------------------------------------------%
%
% The implementation is mostly written in Mercury
% (the exception being setarg/3).
% Apart from setarg/3, the C interface is used only
% as a means of providing different implementations
% for different modes of a predicate and for doing
% unchecked type casts (and inst/mode casts).
%
%---------------------------------------------------------------------------%


:- pragma c_header_code("
/* XXX should change the name in runtime/mercury_trail.h */
#ifndef MR_trail_current_value
  #define MR_trail_current_value(addr) MR_trail_value_at_address((addr))
#endif

/* XXX this should go in runtime/tags.h */
#ifndef strip_tag
  #ifdef HIGH_TAGS
    #define strip_tag(w)	((w) & ((~(Word)0) >> TAGBITS))
  #else
    #define strip_tag(w)	((w) & ((~(Word)0) << TAGBITS))
  #endif
#endif
").

/* impure */
:- pred destructively_update_binding(var_rep(T), var_rep(T)).
:- mode destructively_update_binding(in(ptr(var_rep_any)), in(var_rep_any))
	is det.

destructively_update_binding(DereffedVar, NewBinding) :-
	setarg(DereffedVar, 1, NewBinding).

/*
** setarg/3 provides non-logical backtrackable destructive update.
**
** WARNING: setarg/3 uses side-effects and is not type-safe!
**          Also it does not work for types with exactly one
**	    functor that has exactly one arg.
**          Use only with great care!
*/
/* impure */
:- pred setarg(T1, int, T2).
:- mode setarg(in(any), in, in(any)) is det.
:- pragma c_code(
	setarg(MercuryTerm::in(any), ArgNum::in, NewValue::in(any)),
	will_not_call_mercury,
"{
	Word *ptr = (Word *) strip_tag(MercuryTerm); /* strip off tag bits */
	MR_trail_current_value(&ptr[ArgNum - 1]);
	ptr[ArgNum - 1] = NewValue;
}").

:- type var(T) ---> var_rep(var_rep(T))
	where equality is (==).

:- type var_rep(T)
	--->	free
	;	free(list(pred(T)))	% list of delayed goals
	;	alias(var_rep(T))
	;	ground(T).

:- inst var_rep_any =
	bound(	free
	;	free(list_skel(pred(in) is semidet))
	;	alias(var_rep_any)
	;	ground(ground)
	).
:- inst var_rep_ground =
	bound(	alias(var_rep_ground)
	;	ground(ground)
	).
:- inst var_rep_deref =
	bound(	free
	;	free(list_skel(pred(in) is semidet))
	;	ground(ground)
	).
:- inst ptr(I) = bound(alias(I)).
:- inst uniq_ptr(I) = unique(alias(I)).

%-----------------------------------------------------------------------------%

:- pragma c_code(init(Var::out(any)), may_call_mercury, "
	Var = ML_var_alias(TypeInfo_for_T, ML_var_free(TypeInfo_for_T));
").

/*
% the compiler generates wrong code for this --
% the output is not unique
:- pred var__rep_init(var_rep(T)::out(uniq_ptr(var_rep_any))) is det.
:- pragma export(var__rep_init(out(uniq_ptr(var_rep_any))), "ML_var_init").
var__rep_init(alias(free)).
*/

:- func var__rep_free = (var_rep(T)::out(var_rep_any)) is det.
:- pragma export(var__rep_free = out(var_rep_any), "ML_var_free").
var__rep_free = free.

:- func var__rep_alias(var_rep(T)::in(var_rep_any)) =
		(var_rep(T)::out(var_rep_any)) is det.
:- pragma export(var__rep_alias(in(var_rep_any)) = out(var_rep_any),
		"ML_var_alias").
var__rep_alias(T) = alias(T).

%-----------------------------------------------------------------------------%

/****
:- pragma c_code( var(Value::(free -> clobbered_any)) = (Var::out(any)), % det
	may_call_mercury,
"
	* Value unused *
	ML_var_init(&Var);
").
****/

:- pragma c_code( var(Value::in) = (Var::out) /* det */,
	may_call_mercury,
"
	ML_var_init_with_value(TypeInfo_for_T, Value, &Var);
").

:- pred var__rep_init_with_value(T::in, var_rep(T)::out(var_rep_ground)) is det.
:- pragma export(var__rep_init_with_value(in, out(var_rep_ground)),
	"ML_var_init_with_value").
var__rep_init_with_value(Value, ground(Value)).

:- pragma c_code( var(Value::out) = (Var::in) /* det */, may_call_mercury,
"
	ML_var_get_value(TypeInfo_for_T, Var, &Value);
").

:- pred var__rep_get_value(var_rep(T)::in(var_rep_ground), T::out) is det.
:- pragma export(var__rep_get_value(in(var_rep_ground), out),
	"ML_var_get_value").
var__rep_get_value(ground(Value), Value).
var__rep_get_value(alias(Var), Value) :-
	var__rep_get_value(Var, Value).

:- pragma c_code( var(Value::in) = (Var::in) /* semidet */,
	may_call_mercury,
"
	SUCCESS_INDICATOR = ML_var_test_value(TypeInfo_for_T, Var, Value);
").

:- pred var__rep_test_value(var_rep(T)::in(var_rep_ground), T::in) is semidet.
:- pragma export(var__rep_test_value(in(var_rep_ground), in),
	"ML_var_test_value").
var__rep_test_value(Var, Value) :-
	var__rep_get_value(Var, VarValue),
	Value = VarValue.

:- pragma c_code( var(Value::in) = (Var::in(any)) /* semidet */,
	may_call_mercury,
"
	SUCCESS_INDICATOR = ML_var_unify_with_val(TypeInfo_for_T, Value, Var);
").

:- pred var__rep_unify_with_val(T, var_rep(T)).
:- mode var__rep_unify_with_val(in, in(ptr(var_rep_any))) is semidet.
:- pragma export(var__rep_unify_with_val(in, in(ptr(var_rep_any))),
	"ML_var_unify_with_val").
var__rep_unify_with_val(Value, Var) :-
	deref_var(Var, DereffedVar),
	DereffedVar = alias(OldVarRep),
	( OldVarRep = ground(OldValue) ->
		Value = OldValue
	;
		var__rep_bind_var(OldVarRep, Value, NewVarRep),
		destructively_update_binding(DereffedVar, NewVarRep)
	).

:- pred var__rep_bind_var(var_rep(T)::in(var_rep_any), T::in,
				var_rep(T)::out(var_rep_ground)) is semidet.
var__rep_bind_var(free, Value, ground(Value)).
var__rep_bind_var(free(DelayedGoals), Value, ground(Value)) :-
	call_delayed_goals(DelayedGoals, Value).

%-----------------------------------------------------------------------------%

:- pred call_delayed_goals(list(pred(T)), T).
:- mode call_delayed_goals(in(list_skel(pred(in) is semidet)), in) is semidet.

call_delayed_goals([], _).
call_delayed_goals([Goal|Goals], Value) :-
	call_delayed_goals(Goals, Value),
	call(Goal, Value).

%-----------------------------------------------------------------------------%

	% Beware, the implementation of this is rather unsafe,
	% if the compiler does strange things... 
	% might be better to just implement it in C.
:- pred deref_var(var_rep(T), var_rep(T)).
:- mode deref_var(in(ptr(var_rep_any)), out(ptr(var_rep_any))).
			       % really out(ptr(var_rep_deref)).
deref_var(Var, DerefVar) :-
	Var = alias(Deref1),
	(if Deref1 = alias(_) then
		deref_var(Deref1, DerefVar)
	else
		DerefVar = Var
	).

%-----------------------------------------------------------------------------%

/*
:- pred freeze(var(T), pred(T)).
:- mode freeze(in(any), pred(in) is semidet) is semidet.
:- mode freeze(out(any), pred(in) is semidet) is semidet.
*/

:- pragma c_code(
	freeze(Var::in(any), Pred::(pred(in) is semidet)) /* semidet */,
	may_call_mercury,
"
	ML_var_freeze_in(TypeInfo_for_T, Var, Pred);
").
:- pragma c_code(
	freeze(Var::out(any), Pred::(pred(in) is semidet)) /* semidet */,
	may_call_mercury,
"
	ML_var_freeze_out(TypeInfo_for_T, &Var, Pred);
").


:- pred var__rep_freeze_out(var_rep(T), pred(T)).
:- mode var__rep_freeze_out(out(ptr(var_rep_any)), pred(in) is semidet)
	is semidet.
:- pragma export(
	var__rep_freeze_out(out(ptr(var_rep_any)), pred(in) is semidet),
	"ML_var_freeze_out").

var__rep_freeze_out(Var, Pred) :-
	Var = alias(free([Pred])).

:- pred var__rep_freeze_in(var_rep(T), pred(T)).
:- mode var__rep_freeze_in(in(ptr(var_rep_any)), pred(in) is semidet) is semidet.
:- pragma export(
	var__rep_freeze_in(in(ptr(var_rep_any)), pred(in) is semidet),
	"ML_var_freeze_in").

var__rep_freeze_in(Var, Pred) :-
	deref_var(Var, DereffedVar),
	DereffedVar = alias(OldRep),
	( OldRep = ground(Value) ->
		call(Pred, Value)
	;
		( OldRep = free(OldPreds) ->
			NewPreds = [Pred | OldPreds]
		;
			NewPreds = [Pred]
		),
		NewRep = free(NewPreds),
		/* impure */ destructively_update_binding(DereffedVar, NewRep)
	).

%-----------------------------------------------------------------------------%

:- import_module require.
_ == _ :-
	error("var:'==' not yet implemented").

%-----------------------------------------------------------------------------%

-- 
Fergus Henderson <fjh at cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3         |     -- the last words of T. S. Garp.



More information about the developers mailing list