proposed addition to standard library
Fergus Henderson
fjh at cs.mu.oz.au
Mon Apr 28 23:11:55 AEST 1997
Hi,
Here's a more fleshed out version of the object identity stuff I described
in previous mail. I'm thinking of adding this to the standard library.
(I just need to extend the implementation to work for non-gc grades;
that will require making `object(T)' a builtin type with its own type_layout,
so that we handle deep_copy for it correctly. Also we'd want io__write
to handle objects nicely.)
Comments?
What do people think about using the name `object' for this?
Any suggestions for an alternative name?
%-----------------------------------------------------------------------------%
% Copyright (C) 1997 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.
%-----------------------------------------------------------------------------%
% file: object.m
% This file provides the `object(T)' ADT, which is used for
% immutable objects with identity.
% Useful for hash consing, amoung other things.
% author: fjh
% stability: high
%-----------------------------------------------------------------------------%
:- module object.
:- interface.
% An object(T) is an abstract thing that
% has an identity, and a value of type T.
% Objects with the same identity have the same value,
% but objects with the same value can have different
% identities. However, for each value there is
% a canonical object with that value.
% Objects are unifiable and comparable;
% those operations are both constant time, since they need
% only compare the object identities, not the values.
:- type object(T).
% `object_value(Object) = Value' is true iff
% `Object' is an object with value `Value'.
:- func object_value(object(T)) = T.
:- mode object_value(in) = out is det. % this is O(1)
:- mode object_value(out) = in is cc_multi. % this is also O(1)
% `canonical_object(Value)' returns the canonical object for the
% specified value.
% Warning: such objects will NOT be garbage collected.
:- func canonical_object(T) = object(T).
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module map, std_util.
% Defining object(T) as c_pointer will only work with the
% conservative garbage collector. For non-gc (or accurate-gc)
% grades, deep_copy will not work correctly.
:- type object(T) == c_pointer.
:- pragma c_code(object_value(Obj::in) = (Val::out), will_not_call_mercury, "
Val = Obj;
").
:- pragma c_code(object_value(Obj::out) = (Val::in), will_not_call_mercury, "
Obj = Val;
").
% the following function is not referentially transparent
% it is a det version of the reverse mode of object_value
:- func value_to_object(T) = object(T).
:- pragma c_code(value_to_object(Val::in) = (Obj::out), will_not_call_mercury,
"
Obj = Val;
").
canonical_object(Value) = CanonicalObject :-
%
% The calls to `get_global_table' and `set_global_table' below
% are non-logical. We pass Object to set_global_table and return
% it as CanonicalObject just to try to make sure that the compiler
% won't optimize away the call to set_global_table.
%
% XXX we ought to be using hash tables here...
%
type_to_univ(Value, UnivValue),
get_global_table(Table0),
( map__search(Table0, UnivValue, Object) ->
CanonicalObject = Object
;
Object = value_to_object(Value),
map__det_insert(Table0, UnivValue, Object, Table),
set_global_table(Table, Object, CanonicalObject)
).
:- type canonical_object_table == map(univ, c_pointer).
:- pred get_global_table(canonical_object_table::uo) is det.
:- pred set_global_table(canonical_object_table::in, T::in, T::out) is det.
:- pragma c_header_code("
extern Word ML_canonical_object_table;
").
:- pragma c_code("
#define ML_EMPTY_MAP 0 /* works now, but not very robust */
Word ML_canonical_object_table = ML_EMPTY_MAP;
").
:- pragma c_code(get_global_table(Table::uo), will_not_call_mercury,
"Table = ML_canonical_object_table;"
).
:- pragma c_code(set_global_table(Table::in, In::in, Out::out),
will_not_call_mercury, "
ML_canonical_object_table = Table;
Out = In;
").
/********************
OLD VERSION
% This implementation uses tabling.
:- table canonical_object/1.
canonical_object(Value) = assert_unique(canonical_object_2(Value)).
:- pred canonical_object_2(T::in, object(T)::out) is cc_multi.
canonical_object_2(object_value(Object), Object).
:- func assert_unique(pred(T)) = T.
:- mode assert_unique(pred(out) is cc_nondet) = out is semidet.
:- mode assert_unique(pred(out) is cc_multi) = out is det.
assert_unique(Pred) = OutVal :-
call(cc_cast(Pred), OutVal).
:- func cc_cast(pred(T)) = pred(T).
:- mode cc_cast(pred(out) is cc_nondet) = out(pred(out) is semidet) is det.
:- mode cc_cast(pred(out) is cc_multi) = out(pred(out) is det) is det.
:- pragma c_code(cc_cast(X::(pred(out) is cc_multi)) =
(Y::out(pred(out) is det)),
will_not_call_mercury,
"Y = X;").
:- pragma c_code(cc_cast(X::(pred(out) is cc_nondet)) =
(Y::out(pred(out) is semidet)),
will_not_call_mercury,
"Y = X;").
END OLD VERSION
********************/
%-----------------------------------------------------------------------------%
--
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