for review: add lazy evaluation to library

Fergus Henderson fjh at cs.mu.OZ.AU
Wed Mar 10 11:12:47 AEDT 1999


Hi,

I propose adding support for optional lazy evaluation to the
standard library.  Comments?
I'd appreciate any feedback.

And could someone please review my change?

-------------------

library/lazy.m:
	New module, for lazy evaluation.

NEWS:
w3/news/newsdb.inc:
	Mention the new module.

Index: NEWS
===================================================================
RCS file: /home/mercury1/repository/mercury/NEWS,v
retrieving revision 1.132
diff -u -u -r1.132 NEWS
--- NEWS	1998/12/06 06:23:38	1.132
+++ NEWS	1999/03/09 23:53:46
@@ -1,6 +1,13 @@
 NEWS since Mercury release 0.8:
 -------------------------------
 
+* We've added library support for optional lazy evaluation.
+
+  The Mercury standard library now includes a new module `lazy',
+  which provides support for optional lazy evaluation
+  via a type `lazy(T)', with `delay' and `force' operations.
+  See the Mercury Library Reference Manual for details.
+
 * We've added a new predicate to the Mercury standard library:
 	bag__count_value/3.	
 
@@ -9,4 +16,9 @@
   The interface is based on the C functions dlopen(), dlsym(), and co.,
   which are supported by most modern Unix systems.
   See the files in extras/dynamic_linking for details.
+
+* The debugger now supports interactive queries.
+
+  See the "Interactive query commands" subsection of the "Debugger commands"
+  section of the "Debugging" chapter of the Mercury User's Guide for details.
 
Index: w3/news/newsdb.inc
===================================================================
RCS file: /home/mercury1/repository/w3/news/newsdb.inc,v
retrieving revision 1.22
diff -u -u -r1.22 newsdb.inc
--- newsdb.inc	1999/03/07 14:02:35	1.22
+++ newsdb.inc	1999/03/09 23:50:58
@@ -18,6 +18,13 @@
 
 $newsdb = array(
 
+"9 Mar 1999" => array("Lazy evaluation",
+"The Mercury standard library now includes a new module \`lazy',
+which provides support for optional lazy evaluation,
+via a type \`lazy(T)', with \`delay' and \`force' operations.
+See the 
+<A HREF=\"http://www.cs.mu.oz.au/research/mercury/information/doc/library_toc.html\">Mercury Library Reference Manual</A> for details.
+
 "8 Mar 1999" => array("Interactive queries",
 
 "The Mercury debugger now includes support for interactive queries.
Index: library.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/library.m,v
retrieving revision 1.43
diff -u -u -r1.43 library.m
--- library.m	1998/09/29 05:10:45	1.43
+++ library.m	1999/03/10 00:00:01
@@ -27,7 +27,8 @@
 :- import_module bimap, bintree, bintree_set, bool.
 :- import_module bt_array, char, dir, eqvclass, float.
 :- import_module math, getopt, graph, group, int.
-:- import_module io, list, map, multi_map, pqueue, queue, random, relation.
+:- import_module io, list, lazy, map, multi_map.
+:- import_module pqueue, queue, random, relation.
 :- import_module require, set, set_bbbtree, set_ordlist, set_unordlist, stack.
 :- import_module std_util, string, term, term_io, tree234, varset.
 :- import_module store, rbtree, parser, lexer, ops.


library/lazy.m:
%-----------------------------------------------------------------------------%
% Copyright (C) 1999 The University of Melbourne.
% This file may only be copied under the terms of the GNU Library General
% Public License - see the file COPYING.LIB in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% lazy - provides support for optional lazy evaluation.
%
% Author: fjh.
% Stability: medium.
%
%-----------------------------------------------------------------------------%

:- module lazy.
:- interface.

% a lazily-evaluted value of type T
:- type lazy(T).

% convert a value from type T to lazy(T)
:- func val(T) = lazy(T).
:- mode val(in) = out(lazy) is det.

% construct a lazily-evaluated lazy(T) from a closure.
:- func delay((func) = T) = lazy(T).
:- mode delay((func) = out is det) = out(lazy) is det.

% force the evaluation of a lazy(T), and return the result as type T.
:- func force(lazy(T)) = T.
:- mode force(in(lazy)) = out is det.

% The following may be needed occaisionally, in case
% the compiler can't infer the right higher-order inst...
:- func inst_cast(lazy(T)) = lazy(T).
:- mode inst_cast(in) = out(lazy) is det.

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

:- implementation.
:- interface.

% implementation details
:- inst lazy(I) ---> value(I) ; closure((func) = out(I) is det).
:- inst lazy == lazy(ground).

:- implementation.

% Note that we use a user-defined equality predicate to ensure
% that unifying two lazy(T) values will do the right thing.
:- type lazy(T) ---> value(T) ; closure((func) = T)
	where equality is equal_values.

:- pred equal_values(lazy(T), lazy(T)).
:- mode equal_values(in, in) is semidet.
equal_values(X, Y) :-
	force(inst_cast(X)) = force(inst_cast(Y)).

:- pragma c_code(inst_cast(F::in) = (F2::out(lazy)),
	[will_not_call_mercury, thread_safe], "F = F2;").

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

val(X) = value(X).
delay(F) = closure(F).

% If the compiler were to evaluate calls to delay/1 at compile time,
% it could put the resulting closure/1 term in read-only memory,
% which would make destructively updating it rather dangerous.
% So we'd better not let the compiler inline delay/1.
:- pragma no_inline(delay/1).

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

% The call to promise_only_solution is needed to tell the
% compiler that force will return equal answers given
% arguments that are equal but that have different representations.
force(Lazy) = promise_only_solution(do_force(Lazy)).

:- pred do_force(lazy(T), T).
:- mode do_force(in(lazy), out) is cc_multi.

% The pragma promise_pure is needed to tell the compiler that
% do_force is pure, even though it calls impure code.
:- pragma promise_pure(do_force/2).

do_force(Lazy, Value) :-
	(	
		Lazy = value(Value)
	;
		Lazy = closure(Func),
		Value = apply(Func),

		% Destructively update the closure with a new
		% closure that immediately returns the same value,
		% to avoid having to recompute the same result
		% next time.
		NewFunc = ((func) = Result :- Result = Value),
		impure update_closure(Lazy, NewFunc)
	).

:- impure pred update_closure(T1, T2).
:-        mode update_closure(in, in) is det.

% Note that the implementation of this impure predicate relies on
% some implementation details of the Mercury implementation.
:- pragma c_code(
	update_closure(MercuryTerm::in, NewValue::in),
	[will_not_call_mercury],
"{
	/* strip off tag bits */
	Word *ptr = (Word *) strip_tag(MercuryTerm);
	/* destructively update value */
	*ptr = NewValue;
}").

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

-- 
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