[mercury-users] polymorphism

Fergus Henderson fjh at cs.mu.oz.au
Fri Sep 26 14:04:04 AEST 1997

Tomas By, you wrote:
> I'm writing an XML library. The interface consists of a
> document type and procedures to read/write/validate etc.
> The problem with XML documents is that apps might want to
> treat them either as a stream of elements or as a tree
> structure, and that you sometimes want to preserve
> whitespace and sometimes not.
> So you need four different types:
>    stream with whitespace            [ stream(byte) ]
>    stream without whitespace         [ stream(word) ]
>    tree with whitespace              [ tree(byte) ]
>    tree without whitespace           [ tree(word) ]
> "Fergus Henderson" <fjh at cs.mu.oz.au> wrote:
> > Why don't you want p/1 to handle things of other types?
> I want a procedure 'dump' for example that can handle all the
> four types above. The reason I don't want it to handle other
> things is I don't want to write the code. :-)

Well, one way to handle this is to have `dump' take a couple of
of predicates `DumpToken' and `DumpV' as additional arguments.

	:- pred dump(item(V, T)::in,
			pred(V, io__state, io__state)::(pred(in, di, uo) is det)
			pred(T, io__state, io__state)::(pred(in, di, uo) is det)
			io__state::di, io__state::uo) is det.

	dump(v(V), DumpV, _DumpToken) --> DumpV(V).
	dump(token(Token), _DumpV, DumpToken) --> DumpToken(Token).
	dump(spec(Spec), _DumpV, _DumpToken) --> ...

Then if you want to dump a variable of type stream(byte), you call
`dump(ByteStream, dump_stream, dump_byte)':

	dump_byte_stream(ByteStream) -->
		dump(ByteStream, dump_stream, dump_byte).

This is basically the type-class approach, except that since we don't
yet support type classes, you simulate them using higher-order preds.

The other alternative is that you could just define `item' as a type
that can be either a tree or a stream, of either bytes or words:

	:- type item
		--->	token(token)
		;	spec(spec)
		;	start_tag(string,list(attribute))
		;	end_tag(string)
		;	element(string,list(attribute),item).
	:- type token
		--->	byte(byte)
		;	word(word).

You could then use insts as subtypes:

	:- inst stream(I) == list_skel(stream(I)).
	:- inst stream_item(I)
		--->	token(I)
		;	spec(ground)
		;	start_tag(ground,ground)
		;	end_tag(ground).

	:- inst tree(I) == list_skel(tree(I)).
	:- inst tree_item(I)
		--->	token(I)
		;	spec(ground)
		;	element(ground,ground,tree_item(I)).

	:- inst word_token
		--->	byte(ground).
	:- inst byte_token
		--->	byte(ground).

Another approach is to use the item/0 type defined above,
but not bother with using insts as subtypes -- subtype errors
would then be detected at runtime (via calls to error/1) rather
than at compile time.

I'm really not sure which approach would be better;
I think any of these three approaches could work well.
Probably I'd be inclined to go with the simulate-type-classes approach.

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