for review: abstract instance declarations

Fergus Henderson fjh at cs.mu.OZ.AU
Tue Feb 9 20:52:13 AEDT 1999


Estimated hours taken: 9

Implement abstract instance declarations.

compiler/prog_data.m:
	Rename the `instance_interface' type as `instance_body',
	and make it a discriminated union: either `abstract',
	or `concrete(Methods)'.

compiler/prog_io_typeclass.m:
compiler/hlds_data.m:
compiler/hlds_out.m:
compiler/equiv_type.m:
compiler/check_typeclass.m:
	Change the code to reflect the new name and representation
	of `instance_interface'.

compiler/prog_io_typeclass.m:
	Parse abstract instance declarations.

compiler/make_hlds.m:
	Clean up the code a bit, and make sure that it detects some errors
	which previously we didn't detect: duplicate or overlapping
	instance declarations, and instance declarations with methods for
	classes with no methods.

compiler/check_typeclass.m:
	If an instance is abstract, then we don't need to check
	that the methods in the instance body match those in the class.

compiler/base_typeclass_info.m:
	Only generate base_typeclass_infos for concrete instance
	declarations, not for abstract ones.

doc/reference_manual.texi:
	Document the change.

tests/hard_coded/typeclasses/Mmakefile:
tests/hard_coded/typeclasses/abstract_instance.m:
tests/hard_coded/typeclasses/use_abstract_instance.m:
tests/hard_coded/typeclasses/use_abstract_instance.exp:
tests/invalid/Mmakefile:
tests/invalid/typeclass_test_9.m:
tests/invalid/typeclass_test_9.err_exp:
	Some test cases.

tests/hard_coded/typeclasses/Mmakefile:
	Uncomment typeclass_test_5.m, since we pass that now
	(and have done so for quite some time).

Index: compiler/base_typeclass_info.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/base_typeclass_info.m,v
retrieving revision 1.10
diff -u -b -r1.10 base_typeclass_info.m
--- base_typeclass_info.m	1999/02/04 14:58:08	1.10
+++ base_typeclass_info.m	1999/02/09 04:03:00
@@ -75,9 +75,10 @@
 	base_typeclass_info__gen_infos_for_instance_list(ClassId - Is,
 		ModuleName, ModuleInfo, CModules1),
 	InstanceDefn = hlds_instance_defn(ImportStatus, _TermContext,
-				InstanceConstraints, InstanceTypes, _Interface,
+				InstanceConstraints, InstanceTypes, Body,
 				PredProcIds, _Varset, _SuperClassProofs),
 	(
+		Body = concrete(_),
 			% Only make the base_typeclass_info if the instance
 			% declaration originally came from _this_ module.
 		status_defined_in_this_module(ImportStatus, yes)
@@ -108,8 +109,8 @@
 			Status, Rvals, Procs),
 		CModules = [CModule | CModules1]
 	;
-			% The instance decl is from another module, so
-			% we don't bother including it.
+			% The instance decl is from another module,
+			% or is abstract, so we don't bother including it.
 		CModules = CModules1
 	).
 
Index: compiler/check_typeclass.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/check_typeclass.m,v
retrieving revision 1.20
diff -u -b -r1.20 check_typeclass.m
--- check_typeclass.m	1998/12/06 23:42:59	1.20
+++ check_typeclass.m	1999/02/09 09:45:13
@@ -133,30 +133,53 @@
 
 check_class_instance(ClassId, SuperClasses, Vars, ClassInterface, ClassVarSet,
 		PredIds, InstanceDefn0, InstanceDefn, 
-		ModuleInfo0, ModuleInfo):-
+		Errors0 - ModuleInfo0, Errors - ModuleInfo):-
 		
-		% check conformance of the instance interface
+		% check conformance of the instance body
+	InstanceDefn0 = hlds_instance_defn(_, _, _, _, InstanceBody, _, _, _),
 	(
-		PredIds \= []
-	->
+		InstanceBody = abstract,
+		InstanceDefn1 = InstanceDefn0,
+		ModuleInfo1 = ModuleInfo0,
+		Errors2 = Errors0
+	;
+		InstanceBody = concrete(Methods),
 		list__foldl2(
 			check_instance_pred(ClassId, Vars, ClassInterface), 
 			PredIds, InstanceDefn0, InstanceDefn1,
-			ModuleInfo0, ModuleInfo1)
+			Errors0 - ModuleInfo0, Errors1 - ModuleInfo1),
+		%
+		% Check if there are any instance methods left over,
+		% for which we did not produce a pred_id/proc_id;
+		% if there are any, the instance declaration must have
+		% specified some methods that don't occur in the class.
+		%
+		InstanceDefn1 = hlds_instance_defn(_, Context, _, _,
+				_, MaybePredProcs, _, _),
+		(
+			MaybePredProcs = yes(PredProcs),
+			list__same_length(PredProcs, Methods)
+		->
+			Errors2 = Errors1
 	;
-		% there are no methods for this class
-		InstanceDefn0 = hlds_instance_defn(A, B, C, D, E,
-				_MaybeInstancePredProcs, G, H),
-		InstanceDefn1 = hlds_instance_defn(A, B, C, D, E,
-				yes([]), G, H),
-		ModuleInfo1 = ModuleInfo0
+			ClassId = class_id(ClassName, ClassArity),
+			prog_out__sym_name_to_string(ClassName,
+				ClassNameString),
+			string__int_to_string(ClassArity, ClassArityString),
+			string__append_list([
+				"In instance declaration for `",
+				ClassNameString, "/", ClassArityString, "': ",
+				"incorrect method name(s)."],
+				NewError),
+			Errors2 = [Context - [words(NewError)] | Errors1]
+		)
 	),
 
-
 		% check that the superclass constraints are satisfied for the
 		% types in this instance declaration
 	check_superclass_conformance(ClassId, SuperClasses, Vars, ClassVarSet,
-		InstanceDefn1, InstanceDefn, ModuleInfo1, ModuleInfo).
+		InstanceDefn1, InstanceDefn,
+		Errors2 - ModuleInfo1, Errors - ModuleInfo).
 
 %----------------------------------------------------------------------------%
 
@@ -268,12 +291,12 @@
 		InstanceDefn, Info0, Info) :-
 	InstanceDefn0 = hlds_instance_defn(A, InstanceContext, 
 				InstanceConstraints, InstanceTypes,
-				InstanceInterface, MaybeInstancePredProcs,
+				InstanceBody, MaybeInstancePredProcs,
 				InstanceVarSet, H),
 	Info0 = instance_method_info(ModuleInfo, PredName, PredArity, 
 		ExistQVars, ArgTypes, ClassContext, ArgModes, Errors0,
 		ArgTypeVars, Status, PredOrFunc),
-	get_matching_instance_names(InstanceInterface, PredOrFunc, MethodName,
+	get_matching_instance_names(InstanceBody, PredOrFunc, MethodName,
 		PredArity, InstanceNames),
 	(
 		InstanceNames = [InstancePredName - Context]
@@ -300,7 +323,7 @@
 			InstancePredProcs = InstancePredProcs1
 		),
 		InstanceDefn = hlds_instance_defn(A, Context, 
-			InstanceConstraints, InstanceTypes, InstanceInterface,
+			InstanceConstraints, InstanceTypes, InstanceBody,
 			yes(InstancePredProcs), InstanceVarSet, H)
 	;
 		InstanceNames = [I1, I2 | Is]
@@ -324,17 +347,10 @@
 			InstanceTypesString),
 		string__append_list([
 			"In instance declaration for `",
-			ClassNameString,
-			"(",
-			InstanceTypesString,
-			")': ",
+			ClassNameString, "(", InstanceTypesString, ")': ",
 			"multiple implementations of type class ",
-			PredOrFuncString,
-			" method `",
-			MethodNameString,
-			"/",
-			PredArityString,
-			"'."],
+			PredOrFuncString, " method `",
+			MethodNameString, "/", PredArityString, "'."],
 			ErrorHeader),
 		I1 = _ - I1Context, 
 		Heading = 
@@ -373,17 +389,10 @@
 			InstanceTypesString),
 		string__append_list([
 			"In instance declaration for `",
-			ClassNameString,
-			"(",
-			InstanceTypesString,
-			")': ",
+			ClassNameString, "(", InstanceTypesString, ")': ",
 			"no implementation for type class ",
-			PredOrFuncString,
-			" method `",
-			MethodNameString,
-			"/",
-			PredArityString,
-			"'."],
+			PredOrFuncString, " method `",
+			MethodNameString, "/", PredArityString, "'."],
 			NewError),
 		Errors = [InstanceContext - [words(NewError)] | Errors0],
 		Info = instance_method_info(ModuleInfo, PredName, PredArity,
@@ -391,18 +400,20 @@
 			ArgTypeVars, Status, PredOrFunc)
 	).
 
-:- pred get_matching_instance_names(list(instance_method), pred_or_func,
+:- pred get_matching_instance_names(instance_body, pred_or_func,
 	sym_name, arity, list(pair(sym_name, prog_context))).
 :- mode get_matching_instance_names(in, in, in, in, out) is det.
 
-get_matching_instance_names(InstanceInterface, PredOrFunc, PredName,
+get_matching_instance_names(InstanceBody, PredOrFunc, PredName,
 	PredArity, InstanceNames) :-
 	(
 		PredOrFunc = predicate,
 		solutions(
 			lambda([Pair::out] is nondet, 
 				(
-					list__member(Method, InstanceInterface),
+					InstanceBody =
+						concrete(InstanceMethods),
+					list__member(Method, InstanceMethods),
 					Method = pred_instance(PredName, 
 							SymName, PredArity,
 							Context),
@@ -415,7 +426,9 @@
 		solutions(
 			lambda([Pair::out] is nondet, 
 				(
-					list__member(Method, InstanceInterface),
+					InstanceBody =
+						concrete(InstanceMethods),
+					list__member(Method, InstanceMethods),
 					Method = func_instance(PredName, 
 							SymName, FuncArity,
 							Context),
@@ -584,14 +597,10 @@
 		InstanceString),
 	string__append_list(
 		["Introduced_pred_for_",
-		ClassNameString,
-		"__",
-		InstanceString,
-		"____",
-		MethodNameString,
-		"_",
-		PredArityString
-		], 
+		ClassNameString, "__",
+		InstanceString, "____",
+		MethodNameString, "_",
+		PredArityString], 
 		PredNameString),
 	PredName = unqualified(PredNameString).
 
@@ -657,13 +666,9 @@
 			ConstraintsString),
 		string__append_list([
 			"In instance declaration for `",
-			ClassNameString,
-			"(",
-			InstanceTypesString,
-			")': ",
+			ClassNameString, "(", InstanceTypesString, ")': ",
 			"superclass constraint(s) not satisfied: ",
-			ConstraintsString,
-			"."],
+			ConstraintsString, "."],
 			NewError),
 		Errors = [Context - [words(NewError)] | Errors0],
 		InstanceDefn = InstanceDefn0
Index: compiler/equiv_type.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/equiv_type.m,v
retrieving revision 1.17
diff -u -b -r1.17 equiv_type.m
--- equiv_type.m	1998/11/20 04:07:32	1.17
+++ equiv_type.m	1999/02/09 02:53:35
@@ -155,10 +155,10 @@
 
 equiv_type__replace_in_item(
 			instance(Constraints0, ClassName, Ts0, 
-				InstanceInterface, VarSet0),
+				InstanceBody, VarSet0),
 			EqvMap,
 			instance(Constraints, ClassName, Ts, 
-				InstanceInterface, VarSet),
+				InstanceBody, VarSet),
 			no) :-
 	equiv_type__replace_in_class_constraint_list(Constraints0, VarSet0, 
 				EqvMap, Constraints, VarSet1),
Index: compiler/hlds_data.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/hlds_data.m,v
retrieving revision 1.30
diff -u -b -r1.30 hlds_data.m
--- hlds_data.m	1998/11/24 03:57:03	1.30
+++ hlds_data.m	1999/02/09 05:42:16
@@ -753,7 +753,7 @@
 			prog_context,		% context of declaration
 			list(class_constraint), % Constraints
 			list(type), 		% ClassTypes 
-			instance_interface, 	% Methods
+			instance_body, 		% Methods
 			maybe(hlds_class_interface),
 						% After check_typeclass, we 
 						% will know the pred_ids and
Index: compiler/hlds_out.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/hlds_out.m,v
retrieving revision 1.212
diff -u -b -r1.212 hlds_out.m
--- hlds_out.m	1998/12/06 23:43:18	1.212
+++ hlds_out.m	1999/02/09 06:59:21
@@ -2249,8 +2249,8 @@
 hlds_out__write_instance_defn(Indent, InstanceDefn) -->
 
 	{ InstanceDefn = hlds_instance_defn(_, Context,
-		Constraints, Types, Interface,
-		_MaybeClassInterface, VarSet, Proofs) },
+		Constraints, Types, Body,
+		MaybePredProcIds, VarSet, Proofs) },
 
 	{ term__context_file(Context, FileName) },
 	{ term__context_line(Context, LineNumber) },
@@ -2280,9 +2280,22 @@
 	io__nl,
 
 	hlds_out__write_indent(Indent),
+	(	{ Body = abstract },
+		io__write_string("% abstract")
+	;	{ Body = concrete(Methods) },
 	io__write_string("% Instance Methods: "),
-	mercury_output_instance_methods(Interface),
+		mercury_output_instance_methods(Methods)
+	),
 	io__nl,
+
+	( { MaybePredProcIds = yes(PredProcIds) } ->
+		hlds_out__write_indent(Indent),
+		io__write_string("% procedures: "),
+		io__write(PredProcIds),
+		io__nl
+	;
+		[]
+	),
 
 	hlds_out__write_constraint_proofs(Indent, VarSet, Proofs),
 	io__nl.
Index: compiler/make_hlds.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/make_hlds.m,v
retrieving revision 1.281
diff -u -b -r1.281 make_hlds.m
--- make_hlds.m	1999/02/08 22:42:45	1.281
+++ make_hlds.m	1999/02/09 06:42:00
@@ -508,11 +508,16 @@
 add_item_decl_pass_2(nothing, _, Status, Module, Status, Module) --> [].
 add_item_decl_pass_2(typeclass(_, _, _, _, _)
 	, _, Status, Module, Status, Module) --> [].
-add_item_decl_pass_2(instance(Constraints, Name, Types, Interface, VarSet), 
+add_item_decl_pass_2(instance(Constraints, Name, Types, Body, VarSet), 
 		Context, Status, Module0, Status, Module) -->
 	{ Status = item_status(ImportStatus, _) },
-	module_add_instance_defn(Module0, Constraints, Name, Types, Interface,
-		VarSet, ImportStatus, Context, Module).
+	{ Body = abstract ->
+		make_status_abstract(ImportStatus, BodyStatus)
+	;
+		BodyStatus = ImportStatus
+	},
+	module_add_instance_defn(Module0, Constraints, Name, Types, Body,
+		VarSet, BodyStatus, Context, Module).
 
 %------------------------------------------------------------------------------
 
@@ -1191,13 +1196,7 @@
 	{ convert_type_defn(TypeDefn, Globals, Name, Args, Body) },
 	{ list__length(Args, Arity) },
 	{ Body = abstract_type ->
-		( Status0 = exported ->
-			Status1 = abstract_exported
-		; Status0 = imported ->
-			Status1 = abstract_imported
-		;
-			Status1 = Status0
-		)
+		make_status_abstract(Status0, Status1)
 	;
 		Status1 = Status0
 	},
@@ -1330,6 +1329,18 @@
 		)
 	).
 
+:- pred make_status_abstract(import_status, import_status).
+:- mode make_status_abstract(in, out) is det.
+
+make_status_abstract(Status, AbstractStatus) :-
+	( Status = exported ->
+		AbstractStatus = abstract_exported
+	; Status = imported ->
+		AbstractStatus = abstract_imported
+	;
+		AbstractStatus = Status
+	).
+
 :- pred combine_status(import_status, import_status, import_status).
 :- mode combine_status(in, in, out) is det.
 
@@ -1764,40 +1775,72 @@
 		Module1, Module).
 
 :- pred module_add_instance_defn(module_info, list(class_constraint), sym_name,
-	list(type), instance_interface, tvarset, import_status, prog_context, 
+	list(type), instance_body, tvarset, import_status, prog_context, 
 	module_info, io__state, io__state).
 :- mode module_add_instance_defn(in, in, in, in, in, in, in, in, out, 
 	di, uo) is det.
 
-module_add_instance_defn(Module0, Constraints, Name, Types, Interface, VarSet,
+module_add_instance_defn(Module0, Constraints, ClassName, Types, Body, VarSet,
 		Status, Context, Module) -->
 	{ module_info_classes(Module0, Classes) },
 	{ module_info_instances(Module0, Instances0) },
 	{ list__length(Types, ClassArity) },
-	{ Key = class_id(Name, ClassArity) },
+	{ ClassId = class_id(ClassName, ClassArity) },
 	(
-		{ map__search(Classes, Key, _) }
+		{ map__search(Classes, ClassId, _) }
 	->
 		{ map__init(Empty) },
-		{ NewValue = hlds_instance_defn(Status, Context, Constraints, 
-			Types, Interface, no, VarSet, Empty) },
-		{ map__lookup(Instances0, Key, Values) },
-		{ map__det_update(Instances0, Key, [NewValue|Values], 
+		{ NewValue = hlds_instance_defn(Status, Context,
+			Constraints, Types, Body, no, VarSet, Empty) },
+		{ map__lookup(Instances0, ClassId, Values) },
+		check_for_overlapping_instances(NewValue, Values, ClassId),
+		{ map__det_update(Instances0, ClassId, [NewValue|Values], 
 			Instances) },
-		{ module_info_set_instances(Module0, Instances, Module) }
+		{ module_info_set_instances(Module0, Instances,
+			Module) }
 	;
-		io__stderr_stream(StdErr),
-		io__set_output_stream(StdErr, OldStream),
-		prog_out__write_context(Context),
-		io__write_string("Error: typeclass `"),
-		prog_out__write_sym_name(Name),
-		io__write_char('/'),
-		io__write_int(ClassArity),
-		io__write_string("' not defined.\n"),
-		io__set_exit_status(1),
-		io__set_output_stream(OldStream, _),
+		undefined_type_class_error(ClassName, ClassArity, Context,
+			"instance declaration"),
 		{ Module = Module0 }
 	).
+
+:- pred check_for_overlapping_instances(hlds_instance_defn,
+		list(hlds_instance_defn), class_id, io__state, io__state).
+:- mode check_for_overlapping_instances(in, in, in, di, uo) is det.
+
+check_for_overlapping_instances(NewValue, Values, ClassId) -->
+	{ IsOverlapping = lambda([(Context - OtherContext)::out] is nondet, (
+		NewValue = hlds_instance_defn(_Status, Context,
+				_, Types, Body, _, VarSet, _),
+		Body \= abstract, % XXX
+		list__member(OtherValue, Values),
+		OtherValue = hlds_instance_defn(_OtherStatus, OtherContext,
+				_, OtherTypes, OtherBody, _, OtherVarSet, _),
+		OtherBody \= abstract, % XXX
+		varset__merge(VarSet, OtherVarSet, OtherTypes,
+				_NewVarSet, NewOtherTypes),
+		type_list_subsumes(Types, NewOtherTypes, _)
+	)) },
+	aggregate(IsOverlapping,
+		report_overlapping_instance_declaration(ClassId)).
+
+:- pred report_overlapping_instance_declaration(class_id, pair(prog_context),
+		io__state, io__state).
+:- mode report_overlapping_instance_declaration(in, in, di, uo) is det.
+
+report_overlapping_instance_declaration(class_id(ClassName, ClassArity),
+		Context - OtherContext) -->
+	io__set_exit_status(1),
+	prog_out__write_context(Context),
+	io__write_string("Error: multiply defined (or overlapping) instance\n"),
+	prog_out__write_context(Context),
+	io__write_string("declarations for class `"),
+        prog_out__write_sym_name(ClassName),
+	io__write_string("/"),
+	io__write_int(ClassArity),
+	io__write_string("'.\n"),
+	prog_out__write_context(OtherContext),
+	io__write_string("Previous instance declaration was here.\n").
 
 %-----------------------------------------------------------------------------%
 
Index: compiler/prog_data.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/prog_data.m,v
retrieving revision 1.43
diff -u -b -r1.43 prog_data.m
--- prog_data.m	1998/12/06 23:44:32	1.43
+++ prog_data.m	1999/02/09 03:01:10
@@ -85,7 +85,7 @@
 		%	ClassMethods, VarNames
 
 	;	instance(list(class_constraint), class_name, list(type),
-			instance_interface, tvarset)
+			instance_body, tvarset)
 		%	DerivingClass, ClassName, Types, 
 		%	MethodInstances, VarNames
 
@@ -341,7 +341,11 @@
 				% Line number of declaration
 	.
 
-:- type instance_interface ==	list(instance_method).
+:- type instance_body
+	--->	abstract
+	;	concrete(instance_methods).
+
+:- type instance_methods ==	list(instance_method).
 
 		% an abstract type for representing a set of
 		% `pragma_c_code_attribute's.
Index: compiler/prog_io_typeclass.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/prog_io_typeclass.m,v
retrieving revision 1.10
diff -u -b -r1.10 prog_io_typeclass.m
--- prog_io_typeclass.m	1998/11/20 04:09:01	1.10
+++ prog_io_typeclass.m	1999/02/09 03:02:36
@@ -354,11 +354,10 @@
 		->
 			Result = Result0
 		;
-			Result0 = ok(instance(_, Name, Types, Interface,
-					VarSet0))
+			Result0 = ok(instance(_, Name, Types, Body, VarSet0))
 		->
-			Result = ok(instance(ConstraintList, Name, Types,
-				Interface, VarSet0))
+			Result = ok(instance(ConstraintList, Name, Types, Body,
+					VarSet0))
 		;
 				% if the item we get back isn't an instance, 
 				% something has gone wrong...
@@ -444,7 +443,7 @@
 		(
 			ErroneousTypes = [],
 			Result = ok(instance([], ClassName,
-				TermTypes, [], TVarSet))
+				TermTypes, abstract, TVarSet))
 		;
 				% XXX We should report an error for _each_
 				% XXX erroneous type
@@ -476,7 +475,7 @@
 				NameString, Types, _, _))
 		->
 			Result = ok(instance(Constraints, NameString, Types,
-				MethodList, TVarSet))
+				concrete(MethodList), TVarSet))
 		;
 				% if the item we get back isn't a typeclass,
 				% something has gone wrong...
Index: doc/reference_manual.texi
===================================================================
RCS file: /home/mercury1/repository/mercury/doc/reference_manual.texi,v
retrieving revision 1.120
diff -u -b -r1.120 reference_manual.texi
--- reference_manual.texi	1999/02/08 22:42:51	1.120
+++ reference_manual.texi	1999/02/09 09:36:35
@@ -2515,10 +2515,10 @@
 Mercury provides support for abstract data types, by allowing the
 definition of a type to be kept hidden, with the interface
 only exporting the type name.
-The interface section may contain definitions of types, typeclasses,
-typeclass instances, data constructors, instantiation states, and
-modes, and declarations for abstract data types, functions, predicates,
-and (sub-)modules.
+The interface section may contain definitions of types,
+typeclasses, data constructors, instantiation states, and
+modes, and declarations for abstract data types, abstract typeclass
+instances, functions, predicates, and (sub-)modules.
 The interface section may not contain definitions for functions or
 predicates (i.e. clauses), or definitions of (sub-)modules. 
 
@@ -2527,9 +2527,10 @@
 Any entities declared in this section are local to the module
 (and its sub-modules) and cannot be used by other modules.
 The implementation section must contain definitions
-for all abstract data types, functions, predicates, and
-sub-modules exported by the module,
-as well as for all local types, functions, predicates, and sub-modules.
+for all abstract data types, abstract instance declarations,
+functions, predicates, and sub-modules exported by the module,
+as well as for all local types, typeclass instances, functions,
+predicates, and sub-modules.
 The implementation section can be omitted if it is empty.
 
 The module may optionally end with a @samp{:- end_module @var{ModuleName}}
@@ -2796,6 +2797,7 @@
 @menu
 * Typeclass declarations::
 * Instance declarations::
+* Abstract instance declarations::
 * Type class constraints on predicates and functions::
 * Type class constraints on typeclass declarations::
 * Type class constraints on instance declarations::
@@ -2804,13 +2806,18 @@
 @node Typeclass declarations
 @section Typeclass declarations
 
-A @samp{typeclass} declaration specifies a set of predicates and/or functions
-that must be defined on a type (or set of types) for it (them) to be
-considered to be a member of that type class.
+A @dfn{type class} is a name for a set of types (or a set of sequences of
+types) for which certain predicates and/or functions, called the @dfn{methods}
+of that type class, are defined.
+A @samp{typeclass} declaration defines a new type class, and
+specifies the set of predicates and/or functions
+that must be defined on a type (or sequence of types) for it (them) to be
+considered to be an instance of that type class.
 
-The @code{typeclass} declaration gives the name of the type class, the
+The @code{typeclass} declaration gives the name of the type class that
+it is defining, the
 names of the type variables which are parameters to the type class, and the
-operations ("methods") which form the interface of the type class.
+operations (i.e. methods) which form the interface of the type class.
 
 For example,
 
@@ -2829,8 +2836,8 @@
 @end example
 
 @noindent
-declares the typeclass @code{point}, which refers to points in two dimensional
-space. 
+declares the typeclass @code{point}, which
+represents points in two dimensional space. 
 
 @code{pred}, @code{func} and @code{mode} declarations are the only legal
 declarations inside a @code{typeclass} declaration.  The number of parameters 
@@ -2849,16 +2856,25 @@
 @node Instance declarations
 @section Instance declarations
 
-Once the interface of the typeclass has been declared in the @code{typeclass}
-declaration, we can use an @code{instance} declaration to specify how a
+Once the interface of the typeclass has been defined in the @code{typeclass}
+declaration, we can use an @code{instance} declaration to define how a
 particular type satisfies the interface declared in the @code{typeclass}
 declaration.
 
+An instance declaration has the form
+
+ at example
+:- instance @var{classname}(@var{typename}(@var{typevar}, @dots{}), @dots{})
+        where [pred(@var{methodname}/@var{arity}) is @var{predname},
+               func(@var{methodname}/@var{arity}) is @var{funcname},
+               @dots{}].
+ at end example
+
 An @samp{instance} declaration gives a type for each parameter of the
 typeclass.  Each of these types must be either a type with no arguments, or 
-a polymorphic type whose arguments are all distinct type variables.  e.g. 
- at code{int}, @code{list(T)} and @code{bintree(K,V)} are allowed but
- at code{T}, @code{list(int)} and @code{bintree(T,T)} are not.
+a polymorphic type whose arguments are all distinct type variables.
+For example @code{int}, @code{list(T)} and @code{bintree(K,V)} are allowed,
+but @code{T}, @code{list(int)} and @code{bintree(T,T)} are not.
 The types in an instance declaration must not be abstract types which
 are elsewhere defined as equivalence types.
 A program may not contain more than one @code{instance} declaration for a
@@ -2867,7 +2883,28 @@
 declarations, ie. there is at most one instance declaration that may be 
 applied to any type (or set of types).
 
-For example:
+Each entry in the @samp{where [@dots{}]} part of an @code{instance}
+declaration specifies a binding for one of the methods of the class.
+The @var{predname} or @var{funcname} must name a function or
+predicate of the specified arity whose type, modes, determinism, and
+purity are at least as permissive as the declared type, modes,
+determinism, and purity of the class method with the specified
+ at var{methodname} and @var{arity}, after the types of the arguments
+in the instance declaration have substituted in place of the
+parameters in the typeclass declaration.
+Each @samp{instance} declaration
+must specify a binding for every method declared in the corresponding
+ at samp{class} declaration. 
+
+Any call to a method must have arguments types (and in the case of functions,
+return type) which are constrained to be a member of that method's
+typeclass, or which match one of the instance declarations visible at
+the point of the call.  A method call will invoke the 
+predicate or function specified for that method in the
+instance declaration that matches the types of the arguments
+to the call.
+
+Here's an example of some code using an instance declaration:
 
 @example
 :- type coordinate
@@ -2930,9 +2967,57 @@
 	= coloured_coordinate(X + Dx, Y + Dy, Colour).
 @end example
 
+If we call @samp{translate/3} with the first argument having type
+ at samp{coloured_coordinate}, this will invoke
+ at samp{coloured_coordinate_translate}.
+Likewise, if we call @samp{translate/3} with the first argument having type
+ at samp{coordinate}, this will invoke @samp{coordinate_translate}.
+
 Further instances of the typeclass could be made, e.g. a type which represents
 the point using polar coordinates.
 
+ at node Abstract instance declarations
+ at section Abstract instance declarations
+
+Abstract instance declarations are instance declarations whose
+implementations are hidden.  An abstract instance declaration has the
+same form as an instance declaration, but without the @samp{where
+[@dots{}]} part.  An abstract instance declaration declares that
+certain type(s) are an instance of a particular type class without
+defining how the type class methods are implemented for those type(s).
+Like abstract type declarations,
+abstract instance declarations are only useful in the interface
+section of a module.  Each abstract instance declaration must
+be accompanied by a corresponding non-abstract instance declaration
+that defines how the type class methods are implemented.
+
+Here's an example:
+
+ at example
+:- module hashable.
+:- interface.
+:- import_module int, string.
+
+:- typeclass hashable(T) where [func hash(T) = int].
+:- instance hashable(int).
+:- instance hashable(string).
+
+:- implementation.
+
+:- instance hashable(int) where [func(hash/1) is hash_int].
+:- instance hashable(string) where [func(hash/1) is hash_string].
+
+:- func hash_int(int) = int.
+hash_int(X) = X.
+
+:- func hash_string(string) = int.
+hash_string(S) = H :-
+        % use the standard library predicate string__hash/2
+        string__hash(S, H).
+
+:- end_module hashable.
+ at end example
+
 @node Type class constraints on predicates and functions
 @section Type class constraints on predicates and functions
 
Index: tests/hard_coded/typeclasses/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/tests/hard_coded/typeclasses/Mmakefile,v
retrieving revision 1.13
diff -u -b -r1.13 Mmakefile
--- Mmakefile	1998/10/23 00:41:57	1.13
+++ Mmakefile	1999/02/09 06:36:50
@@ -24,7 +24,9 @@
 	operator_classname \
 	superclass_call \
 	test_default_func_mode \
-	typeclass_test_6
+	typeclass_test_5 \
+	typeclass_test_6 \
+	use_abstract_instance
 
 # These tests are all failing in jump and fast grades b/c we can't use static
 # code initialisers.
@@ -51,16 +53,13 @@
 # Actually, there is a bug, but it isn't in that test case. I'm trying to
 # find exactly how to trigger the bug. Oh well...
 
-# we do not yet pass the following tests:
-#       typeclass_test_5.m (this is really a WISHLIST item)
-#
-
 MCFLAGS-extra_typeinfo = --optimize-higher-order --no-type-specialization \
 				--typeinfo-liveness
 MCFLAGS-inference_test = --infer-all
 MCFLAGS-inference_test_2 = --infer-all
 MCFLAGS-existential_type_classes = --infer-all
 MCFLAGS-lambda_multi_constraint_same_tvar = --infer-all
+MCFLAGS-abstract_instance = --infer-all
 
 #-----------------------------------------------------------------------------#
 
Index: tests/hard_coded/typeclasses/abstract_instance.m
===================================================================
RCS file: abstract_instance.m
diff -N abstract_instance.m
--- /dev/null	Tue Feb  9 20:39:39 1999
+++ abstract_instance.m	Tue Feb  9 17:38:50 1999
@@ -0,0 +1,22 @@
+:- module abstract_instance.
+:- interface.
+:- import_module io, list.
+
+:- typeclass runnable(T) where [
+	pred run(T::in, io__state::di, io__state::uo) is det
+].
+
+:- instance runnable(int).
+:- instance runnable(string).
+:- instance runnable(list(T)) <= runnable(T).
+
+:- implementation.
+
+:- instance runnable(int) where [pred(run/3) is run_int].
+:- instance runnable(string) where [pred(run/3) is run_string].
+:- instance runnable(list(T)) <= runnable(T) where [pred(run/3) is run_list].
+
+run_int(I) --> io__write_int(I), io__nl.
+run_string(S) --> io__write_string(S), io__nl.
+run_list([]) --> [].
+run_list([X|Xs]) --> run(X), run(Xs).
Index: tests/hard_coded/typeclasses/use_abstract_instance.exp
===================================================================
RCS file: use_abstract_instance.exp
diff -N use_abstract_instance.exp
--- /dev/null	Tue Feb  9 20:39:39 1999
+++ use_abstract_instance.exp	Tue Feb  9 17:39:42 1999
@@ -0,0 +1,10 @@
+42
+hello world
+5
+4
+3
+2
+1
+hello
+world
+0
Index: tests/hard_coded/typeclasses/use_abstract_instance.m
===================================================================
RCS file: use_abstract_instance.m
diff -N use_abstract_instance.m
--- /dev/null	Tue Feb  9 20:39:39 1999
+++ use_abstract_instance.m	Tue Feb  9 17:37:50 1999
@@ -0,0 +1,15 @@
+:- module use_abstract_instance.
+:- interface.
+:- import_module io.
+
+:- pred main(io__state::di, io__state::uo) is det.
+
+:- implementation.
+:- import_module abstract_instance, list.
+
+main -->
+	run(42),
+	run("hello world"),
+	run([5,4,3,2,1]),
+	run(["hello", "world"]),
+	run([[[[0]]]]).
Index: tests/invalid/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/tests/invalid/Mmakefile,v
retrieving revision 1.36
diff -u -b -r1.36 Mmakefile
--- Mmakefile	1998/11/09 05:28:38	1.36
+++ Mmakefile	1999/02/09 08:00:32
@@ -54,6 +54,7 @@
 	typeclass_test_4.m \
 	typeclass_test_5.m \
 	typeclass_test_7.m \
+	typeclass_test_9.m \
 	types.m	\
 	unbound_inst_var.m \
 	undef_lambda_mode.m \
@@ -67,6 +68,8 @@
 #	parent.undeclared_child.m (just not yet implemented)
 #	sub_b.m and sub_c.m (bug with dependencies & nested modules)
 #	freefree.m 	(need bromage's aliasing stuff)
+#	typeclass_test_8.m (minor formatting error in the output --
+#			the type class name should be in quotes)
 
 MCFLAGS-multisoln_func	=	--infer-types
 MCFLAGS-any_mode	=	--infer-types
Index: tests/invalid/typeclass_test_9.err_exp
===================================================================
RCS file: typeclass_test_9.err_exp
diff -N typeclass_test_9.err_exp
--- /dev/null	Tue Feb  9 20:39:39 1999
+++ typeclass_test_9.err_exp	Tue Feb  9 20:41:55 1999
@@ -0,0 +1,12 @@
+typeclass_test_9.m:001: In module `typeclass_test_9':
+typeclass_test_9.m:001:   warning: module `std_util'
+typeclass_test_9.m:001:   is imported in the interface, but is not
+typeclass_test_9.m:001:   used in the interface.
+typeclass_test_9.m:010: Error: multiply defined (or overlapping) instance
+typeclass_test_9.m:010: declarations for class `typeclass_test_9:foo/1'.
+typeclass_test_9.m:007: Previous instance declaration was here.
+typeclass_test_9.m:013: In instance declaration for `typeclass_test_9:bar/1':
+typeclass_test_9.m:013:   incorrect method name(s).
+typeclass_test_9.m:018: In instance declaration for `typeclass_test_9:baz/1':
+typeclass_test_9.m:018:   incorrect method name(s).
+For more information, try recompiling with `-E'.
Index: tests/invalid/typeclass_test_9.m
===================================================================
RCS file: typeclass_test_9.m
diff -N typeclass_test_9.m
--- /dev/null	Tue Feb  9 20:39:39 1999
+++ typeclass_test_9.m	Tue Feb  9 20:42:38 1999
@@ -0,0 +1,19 @@
+:- module typeclass_test_9.
+:- interface.
+:- import_module std_util.
+:- typeclass foo(T) where [pred p is semidet].
+:- typeclass bar(T) where [].
+:- typeclass baz(T) where [pred q is semidet].
+:- instance foo(int) where [
+	pred(p/0) is semidet_succeed
+].
+:- instance foo(int) where [
+	pred(p/0) is semidet_fail
+].
+:- instance bar(int) where [
+	pred(p/0) is semidet_fail
+].
+:- instance baz(int) where [
+	pred(r/0) is semidet_fail,
+	pred(q/0) is semidet_fail
+].
-- 
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