for review: change library to use existential types

Fergus Henderson fjh at cs.mu.OZ.AU
Wed Nov 18 20:03:11 AEDT 1998


Hi,

I suppose dgj is the exisential types export -- could you
please review this one for me, David?

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

Change the standard library to use existential types.

library/std_util.m:
	Add new existentially typed procedures univ_value/1 and has_type/2.

library/term.m:
library/io.m:
	Use existential types rather than unsafe casts and other hacks.

Index: library/std_util.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/std_util.m,v
retrieving revision 1.136
diff -u -r1.136 std_util.m
--- std_util.m	1998/11/17 00:57:39	1.136
+++ std_util.m	1998/11/18 08:43:24
@@ -78,6 +78,14 @@
 	%
 :- func univ_type(univ) = type_info.
 
+
+	% univ_value(Univ):
+	%	returns the value of the object stored in Univ.
+	%
+	% Warning: support for existential types is still experimental.
+	%
+:- some [T] func univ_value(univ) = T.
+
 %-----------------------------------------------------------------------------%
 
 % The "maybe" type.
@@ -214,11 +222,21 @@
 	% get_functor/5 and construct/3 will fail if used upon a value
 	% of this type.)
 
-	% type_of(Data) returns a representation of the type of Data.
+	% The function type_of/1 returns a representation of the type
+	% of its argument.
 	%
 :- func type_of(T) = type_info.
 :- mode type_of(unused) = out is det.
 
+	% The predicate has_type/2 is basically an existentially typed
+	% inverse to the function type_of/1.  It constrains the type
+	% of the first argument to be the type represented by the
+	% second argument.
+	%
+	% Warning: support for existential types is still experimental.
+	%
+:- some [T] pred has_type(T::unused, type_info::in) is det.
+
 	% type_name(Type) returns the name of the specified type
 	% (e.g. type_name(type_of([2,3])) = "list:list(int)").
 	% Any equivalence types will be fully expanded.
@@ -970,24 +988,11 @@
 		error(ErrorString)
 	).
 
-/****
-
-% univ_value/1 can't be implemented yet, due to the lack of support for
-% existential types in Mercury.
-
-	% univ_value(Univ):
-	%	returns the value of the object stored in Univ.
-:- some [T] (
-   func univ_value(univ) = T
-).
-
-:- pragma c_code(univ_value(Univ::uo) = (Value), will_not_call_mercury, "
+:- pragma c_code(univ_value(Univ::in) = (Value::out), will_not_call_mercury, "
 	TypeInfo_for_T = field(mktag(0), Univ, UNIV_OFFSET_FOR_TYPEINFO);
-	Value = field(mktag(0), Univ, UNIV_OFFSET_FOR_Data);
+	Value = field(mktag(0), Univ, UNIV_OFFSET_FOR_DATA);
 ").
 
-****/
-
 :- pragma c_header_code("
 /*
 **	`univ' is represented as a two word structure.
@@ -1322,14 +1327,9 @@
 	% small integers.  See runtime/type_info.h.
 :- type type_ctor_info == c_pointer.  
 
-:- pragma c_code(type_of(Value::unused) = (TypeInfo::out),
+:- pragma c_code(type_of(_Value::unused) = (TypeInfo::out),
 	will_not_call_mercury, " 
 {
-	/* 
-	** `Value' isn't used in this c_code, but the compiler
-	** gives a warning if you don't mention it.
-	*/ 
-
 	TypeInfo = TypeInfo_for_T;
 
 	/*
@@ -1345,6 +1345,10 @@
 #endif
 
 }
+").
+
+:- pragma c_code(has_type(_Arg::unused, TypeInfo::in), will_not_call_mercury, "
+	TypeInfo_for_T = TypeInfo;
 ").
 
 % Export this function in order to use it in runtime/mercury_trace_external.c
Index: library/io.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/io.m,v
retrieving revision 1.171
diff -u -r1.171 io.m
--- io.m	1998/11/15 16:47:40	1.171
+++ io.m	1998/11/18 08:43:22
@@ -1792,6 +1792,8 @@
 	io__write(X),
 	io__set_output_stream(OrigStream, _Stream).
 
+%-----------------------------------------------------------------------------%
+
 io__write(Term) -->
 	{ type_to_univ(Term, Univ) },
 	io__write_univ(Univ).
@@ -1808,6 +1810,7 @@
 	% we need to special-case the builtin types:
 	%	int, char, float, string
 	%	type_info, univ, c_pointer, array
+	%	and private_builtin:type_info
 	%
 	( { univ_to_type(Univ, String) } ->
 		term_io__quote_string(String)
@@ -1818,64 +1821,69 @@
 	; { univ_to_type(Univ, Float) } ->
 		io__write_float(Float)
 	; { univ_to_type(Univ, TypeInfo) } ->
-		io__write_string(type_name(TypeInfo))
+		io__write_type_info(TypeInfo)
 	; { univ_to_type(Univ, OrigUniv) } ->
 		io__write_univ_as_univ(OrigUniv)
 	; { univ_to_type(Univ, C_Pointer) } ->
 		io__write_c_pointer(C_Pointer)
-	; { type_ctor_name(type_ctor(univ_type(Univ))) = "type_info" },
-	  { type_ctor_module_name(type_ctor(univ_type(Univ))) =
-			"private_builtin" } ->
-	  	% XXX This is a hack (see the comment for array below).
-		{ TypeInfo = unsafe_cast(univ_value_as_type_any(Univ)) },
-		io__write_string(type_name(TypeInfo))
-	; { type_ctor_name(type_ctor(univ_type(Univ))) = "array" },
-	  { type_ctor_module_name(type_ctor(univ_type(Univ))) = "array" } ->
-		%
-		% Note that we can't use univ_to_type above, because we
-		% want to match on a non-ground type `array(T)'
-		% (matching against `array(void)' isn't much use).
-		% Instead, we explicitly check the type name.
-		% That makes it tricky to get the value, so
-		% we can't use io__write_array below... instead we
-		% use the following, which is a bit of a hack.
-		%
-		{ term__univ_to_term(Univ, Term) },
-		{ varset__init(VarSet) },
-		term_io__write_term(VarSet, Term)
 	;
+		%
+		% Check if the type is array:array/1.
+		% We can't just use univ_to_type here since 
+		% array:array/1 is a polymorphic type.
+		%
+		% The calls to type_ctor_name and type_ctor_module_name
+		% are not really necessary -- we could use univ_to_type
+		% in the condition instead of det_univ_to_type in the body.
+		% However, this way of doing things is probably more efficient
+		% in the common case when the thing being printed is
+		% *not* of type array:array/1.
+		%
+		% The ordering of the tests here (arity, then name, then
+		% module name, rather than the reverse) is also chosen
+		% for efficiency, to find failure cheaply in the common cases,
+		% rather than for readability.
+		%
+		{ type_ctor_and_args(univ_type(Univ), TypeCtor, ArgTypes) },
+		{ ArgTypes = [ElemType] },
+		{ type_ctor_name(TypeCtor) = "array" },
+		{ type_ctor_module_name(TypeCtor) = "array" }
+	->
+		%
+		% Now that we know the element type, we can
+		% constrain the type of the variable `Array'
+		% so that we can use det_univ_to_type.
+		%
+		{ has_type(Elem, ElemType) },
+		{ same_array_elem_type(Array, Elem) },
+		{ det_univ_to_type(Univ, Array) },
+		io__write_array(Array)
+	; 
+		%
+		% Check if the type is private_builtin:type_info/1.
+		% See the comments above for array:array/1.
+		%
+		{ type_ctor_and_args(univ_type(Univ), TypeCtor, ArgTypes) },
+		{ ArgTypes = [ElemType] },
+		{ type_ctor_name(TypeCtor) = "type_info" },
+		{ type_ctor_module_name(TypeCtor) = "private_builtin" }
+	->
+		{ has_type(Elem, ElemType) },
+		{ same_private_builtin_type(PrivateBuiltinTypeInfo, Elem) },
+		{ det_univ_to_type(Univ, PrivateBuiltinTypeInfo) },
+		io__write_private_builtin_type_info(PrivateBuiltinTypeInfo)
+	; 
 		io__write_ordinary_term(Univ, Priority)
 	).
 
-	% XXX These two functions and the type definition 
-	% are just temporary, they are used for the
-	% horrible hack above.
-
-:- func unsafe_cast(T1::in) = (T2::out) is det.
-:- pragma c_code(unsafe_cast(VarIn::in) = (VarOut::out),
-	[will_not_call_mercury, thread_safe], "
-	VarOut = VarIn;
-").
-
-:- type any == c_pointer.
-
-:- func univ_value_as_type_any(univ) = any.
-:- pragma c_code(univ_value_as_type_any(Univ::in) = (Val::out),
-	[will_not_call_mercury, thread_safe], "
-	Val = field(mktag(0), Univ, UNIV_OFFSET_FOR_DATA);
-").
-
-
-:- pred io__write_univ_as_univ(univ, io__state, io__state).
-:- mode io__write_univ_as_univ(in, di, uo) is det.
+:- pred same_array_elem_type(array(T), T).
+:- mode same_array_elem_type(unused, unused) is det.
+same_array_elem_type(_, _).
+
+:- pred same_private_builtin_type(private_builtin__type_info(T), T).
+:- mode same_private_builtin_type(unused, unused) is det.
+same_private_builtin_type(_, _).
 
-io__write_univ_as_univ(Univ) -->
-	io__write_string("univ("),
-	io__write_univ(Univ),
-	% XXX what is the right TYPE_QUAL_OP to use here?
-	io__write_string(" : "),
-	io__write_string(type_name(univ_type(Univ))),
-	io__write_string(")").
 
 :- pred io__write_ordinary_term(univ, ops__priority, io__state, io__state).
 :- mode io__write_ordinary_term(in, in, di, uo) is det.
@@ -2029,6 +2037,32 @@
 	io__write_univ(X),
 	io__write_term_args(Xs).
 
+%-----------------------------------------------------------------------------%
+
+:- pred io__write_type_info(type_info, io__state, io__state).
+:- mode io__write_type_info(in, di, uo) is det.
+
+io__write_type_info(TypeInfo) -->
+	io__write_string(type_name(TypeInfo)).
+
+:- pred io__write_univ_as_univ(univ, io__state, io__state).
+:- mode io__write_univ_as_univ(in, di, uo) is det.
+
+io__write_univ_as_univ(Univ) -->
+	io__write_string("univ("),
+	io__write_univ(Univ),
+	% XXX what is the right TYPE_QUAL_OP to use here?
+	io__write_string(" : "),
+	io__write_string(type_name(univ_type(Univ))),
+	io__write_string(")").
+
+:- pred io__write_c_pointer(c_pointer, io__state, io__state).
+:- mode io__write_c_pointer(in, di, uo) is det.
+
+io__write_c_pointer(_C_Pointer) -->
+	% XXX what should we do here?
+	io__write_string("'<<c_pointer>>'").
+
 :- pred io__write_array(array(T), io__state, io__state).
 :- mode io__write_array(in, di, uo) is det.
 
@@ -2038,12 +2072,18 @@
 	io__write(List),
 	io__write_string(")").
 
-:- pred io__write_c_pointer(c_pointer, io__state, io__state).
-:- mode io__write_c_pointer(in, di, uo) is det.
+:- pred io__write_private_builtin_type_info(private_builtin__type_info(T)::in,
+		io__state::di, io__state::uo) is det.
+io__write_private_builtin_type_info(PrivateBuiltinTypeInfo) -->
+	{ TypeInfo = unsafe_cast(PrivateBuiltinTypeInfo) },
+	io__write_type_info(TypeInfo).
 
-io__write_c_pointer(_C_Pointer) -->
-	% XXX what should we do here?
-	io__write_string("'<<c_pointer>>'").
+:- func unsafe_cast(T1::in) = (T2::out) is det.
+:- pragma c_code(unsafe_cast(VarIn::in) = (VarOut::out),
+	[will_not_call_mercury, thread_safe],
+"
+	VarOut = VarIn;
+").
 
 %-----------------------------------------------------------------------------%
 
Index: library/term.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/term.m,v
retrieving revision 1.86
diff -u -r1.86 term.m
--- term.m	1998/10/02 20:14:52	1.86
+++ term.m	1998/10/27 16:33:35
@@ -434,31 +434,18 @@
 	% convert the term representing the list of elements back to a list,
 	% and then (if successful) we just call the array/1 function.
 	%
-	ListTypeCtor = type_ctor(type_of([0])),
-	ListType = det_make_type(ListTypeCtor, [ElemType]),
+	has_type(Elem, ElemType),
+	ListType = type_of([Elem]),
 	ArgContext = arg_context(term__atom("array"), 1, TermContext),
 	NewContext = [ArgContext | PrevContext],
 	term__try_term_to_univ_2(ArgList, ListType, NewContext, ArgResult),
 	(
 		ArgResult = ok(ListUniv),
-/***************
-% XXX existential types not yet implemented...
-		% :- some [T] pred has_type(T::unused, type_info::in) is det.
-		has_type(List, ListType),
+		has_type(Elem2, ElemType),
+		same_type(List, [Elem2]),
 		det_univ_to_type(ListUniv, List),
 		Array = array(List),
 		Result = ok(univ(Array))
-****************/
-		% since we don't have existential types, we have to use
-		% some unsafe casts...
-		require_equal(univ_type(ListUniv), ListType),
-		list_of_any(List),   % explicit type qualification
-				     % to avoid unbound type variables
-		List = unsafe_cast(univ_value_as_type_any(ListUniv)),
-		Array = array(List),
-		ArrayTypeCtor = type_ctor(type_of(Array)),
-		ArrayType = det_make_type(ArrayTypeCtor, [ElemType]),
-		Result = ok(unsafe_any_to_univ(ArrayType, unsafe_cast(Array)))
 	;
 		ArgResult = error(Error),
 		Result = error(Error)
@@ -476,9 +463,6 @@
 	% ditto
 	fail.
 
-:- pred same_type(T::unused, T::unused) is det.
-same_type(_, _).
-
 :- pred term__term_list_to_univ_list(list(term)::in, list(type_info)::in,
 		term__const::in, int::in, term_to_type_context::in,
 		term__context::in, term_to_type_result(list(univ))::out)
@@ -542,48 +526,6 @@
 
 %-----------------------------------------------------------------------------%
 
-/**********
-XXX existential types not yet implemented
-:- some [T] pred has_type(T::unused, type_info::in) is det.
-:- pragma c_code(has_type(Arg::unused, TypeInfo::in), will_not_call_mercury,
-	"TypeInfo_for_T = TypeInfo;"
-).
-**********/
-
-:- func unsafe_cast(T1::in) = (T2::out) is det.
-:- pragma c_code(unsafe_cast(VarIn::in) = (VarOut::out), will_not_call_mercury,
-	"VarOut = VarIn;").
-
-:- type any == c_pointer.
-
-:- pred array_of_any(array(any)::unused) is det.
-array_of_any(_).
-
-:- pred list_of_any(list(any)::unused) is det.
-list_of_any(_).
-
-:- func univ_value_as_type_any(univ) = any.
-:- pragma c_code(univ_value_as_type_any(Univ::in) = (Val::out),
-		will_not_call_mercury,
-"
-	Val = field(mktag(0), Univ, UNIV_OFFSET_FOR_DATA);
-").
-
-:- func unsafe_any_to_univ(type_info, any) = univ.
-	% Forward mode - convert from type to univ.
-	% Allocate heap space, set the first field to contain the address
-	% of the type_info for this type, and then store the input argument
-	% in the second field.
-:- pragma c_code(unsafe_any_to_univ(TypeInfo::in, Value::in) = (Univ::out),
-		will_not_call_mercury,
-"
-	incr_hp(Univ, 2);
-	field(mktag(0), Univ, UNIV_OFFSET_FOR_TYPEINFO) = (Word) TypeInfo;
-	field(mktag(0), Univ, UNIV_OFFSET_FOR_DATA) = (Word) Value;
-").
-
-%-----------------------------------------------------------------------------%
-
 term__type_to_term(Val, Term) :- type_to_univ(Val, Univ),
 	term__univ_to_term(Univ, Term).
 
@@ -661,36 +603,21 @@
 			[term__functor(term__atom(":"),	 % TYPE_QUAL_OP
 				[ValueTerm, TypeTerm],
 				Context)], Context),
-/****
-XXX existential types not implemented
-	det_univ_to_type(univ_value(Univ), UnivValue),
-****/
-	% instead, we use some unsafe casts...
-	require_equal(univ_type(Univ), type_of(UnivValue)),
-	UnivValue = unsafe_cast(univ_value_as_type_any(Univ)),
-
-	type_info_to_term(Context, univ_type(UnivValue), TypeTerm),
-	term__univ_to_term(UnivValue, ValueTerm).
+	type_info_to_term(Context, univ_type(Univ), TypeTerm),
+	UnivValue = univ_value(Univ),
+	term__type_to_term(UnivValue, ValueTerm).
 
 term__univ_to_term_special_case("array", "array", [ElemType], Univ, Context, 
 		Term) :-
 	Term = term__functor(term__atom("array"), [ArgsTerm], Context),
-	ListTypeCtor = type_ctor(type_of([0])),
-	ListType = det_make_type(ListTypeCtor, [ElemType]),
-/***
-XXX existential types not yet implemented
-	has_type(List, ListType),
-	det__univ_to_type(Univ, Array),	
+	has_type(Elem, ElemType),
+	same_type(List, [Elem]),
+	det_univ_to_type(Univ, Array),	
 	array__to_list(Array, List),
 	term__type_to_term(List, ArgsTerm).
-***/
-	% instead, we need to use some unsafe casts...
-	array_of_any(Array), % explicit type qualification
-			     % to avoid unbound type variables
-	Array = unsafe_cast(univ_value_as_type_any(Univ)),	
-	array__to_list(Array, List),
-	ListUniv = unsafe_any_to_univ(ListType, unsafe_cast(List)),
-	term__univ_to_term(ListUniv, ArgsTerm).
+
+:- pred same_type(T::unused, T::unused) is det.
+same_type(_, _).
 
 :- pred term__univ_list_to_term_list(list(univ)::in,
 				list(term)::out) is det.

-- 
Fergus Henderson <fjh at cs.mu.oz.au>  |  "Binaries may die
WWW: <http://www.cs.mu.oz.au/~fjh>  |   but source code lives forever"
PGP: finger fjh at 128.250.37.3        |     -- leaked Microsoft memo.



More information about the developers mailing list