[mercury-users] Pred defns

Fergus Henderson fjh at cs.mu.OZ.AU
Tue Apr 7 03:22:28 AEST 1998

On 03-Apr-1998, Bart Demoen <bmd at CS.kuleuven.ac.be> wrote:
> > I don't think the cross-module threading problem is much of problem:  just
> > require declarations of the "global variables" that are needed/defined by
> > exported predicates.
> A declaration for global variables needed/defined by exported
> predicates - very strange: it attaches the notion to a predicate,
> which feels quite wrong to me; also, it appears to me that keeping
> "global variables" local to a module (or compilation unit), is the
> sensible thing to do: they are (backtrackable) state attached to a
> module. Implement/specify them with threading variables >only< and be
> prepared for cases you can't do in a satisfactory way without
> introducing a whole lot of ballast (= more declarations).

One way to do that sort of thing would be to provide an extensible
type called say 'gvar'/1, with some way for the user to declare
new constants of this type (or of instances of this type, e.g.
`gvar(int)', `gvar(float)', etc.) in different modules; then the
io__state could contain a global store of the values of these
global variables.

You can emulate this currently, using strings.  Here's the
interface of a library module that provides this functionality 
(the implementation is given at the end of this mail):

	:- module global_vars.
	:- interface.
	:- import_module io.

	% A `gvar(T)' represents the name of a global variable whose
	% corresponding value has type T. 
	% `make_gvar(Name, Val)' is used to create a gvar(T);
	% Name must be a unique string, and Val is used only to specify
	% the type of the gvar.
	:- type gvar(T).
	:- func make_gvar(string::in, T::unused) = (gvar(T)::out) is det.

	% Type `globals' represents the current state of a set of
	% global variables (gvars).
	:- type globals.
	:- func init_globals = globals.
	:- func get_global(gvar(T), globals) = T.
	:- pred get_global(gvar(T)::in, T::out, globals::in, globals::out)
		is det.
	:- pred set_global(gvar(T)::in, T::in, globals::in, globals::out)
		is det.

	% The following predicates provide a way of storing a `globals'
	% state in the io__state.
	:- pred io_init_globals(io__state::di, io__state::uo) is det.
	:- pred io_get_global(gvar(T)::in, T::out, 
			io__state::di, io__state::uo) is det.
	:- pred io_set_global(gvar(T)::in, T::in, 
			io__state::di, io__state::uo) is det.

Then, in the user code you can use this module to declare
new global variables:

	:- module my_module.
	:- interface.
	:- import_module global_vars.

	:- func public_global_var = gvar(float).

	:- implementation.

	:- func private_global_var = gvar(int).

	public_global_var = make_gvar("my_module__public_global_var", 0.0).
	private_global_var = make_gvar("my_module__private_global_var", 0).

As shown by this example, these global variables can be local to a module,
or they can be truly "global".

You can then you can use DCG notation to update the values of these
global variables:

	main -->
		set_global(public_global_var, 3.14),

	subroutine -->
		get_global(public_global_var, X),

The main drawbacks of this approach are that

(1) the code to define a global variable is a little clumsy;
    in particular, the need to call make_gvar/2 with a unique
    string and with a dummy value to get the type right is
    not exactly elegant.

(2) it's not particularly efficient.

However, it would be possible to provide some special syntax
for defining new constants of type gvar/1, eliminating the need
to call make_gvar/2, and to provide a lower-level (built in
to the compiler) implementation which was efficient.

P.S. Here's the implementation of the `global_vars module':

	:- implementation.
	:- import_module map, std_util.

	:- type gvar(T) ---> gvar(string).
	:- type globals == map(string, univ).

	make_gvar(Name, _) = gvar(Name).

	init_globals = Globals :-

	get_global(gvar(Name), Globals) = Val :-
		map__lookup(Globals, Name, Univ),
		det_univ_to_type(Univ, Val).
	get_global(Var, Val, Globals, Globals) :-
		Val = get_global(Var, Globals).
	set_global(gvar(Name), Val, Globals0, Globals) :-
		type_to_univ(Val, Univ),
		map__set(Globals0, Name, Univ, Globals).

	io_init_globals -->
		{ Globals = init_globals },
		{ type_to_univ(Globals, GlobalsUniv) },
		{ copy(GlobalsUniv, UniqGlobalsUniv) },

	io_set_global(Var, Val) -->
		{ det_univ_to_type(GlobalsUniv0, Globals0) },
		{ set_global(Var, Val, Globals0, Globals) },
		{ type_to_univ(Globals, GlobalsUniv) },
		{ copy(GlobalsUniv, UniqGlobalsUniv) },

	io_get_global(Var, Val) -->
		{ det_univ_to_type(GlobalsUniv, Globals) },
		{ Val = get_global(Var, Globals) }.

P.P.S.  This code is untested, but it compiles OK, so I expect it will
	work ;-)

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        |     -- the last words of T. S. Garp.

More information about the users mailing list