[mercury-users] "some" and existential types

Ralph Becket rbeck at microsoft.com
Sat Oct 2 21:35:03 AEST 1999


> From: Michael Day [mailto:mikeday at corplink.com.au]
> 
> I'm a little confused about this part. What exactly is the difference
> between these two declarations:
> 
> :- pred give_me_a_value1(T::out).
> :- some [T] pred give_me_a_value2(T::out).

If we make all quantification explicit, the first becomes

:- all [T] pred give_me_a_value1(T::out).

which says that for any particular T you (i.e. the caller) want, 
give_me_a_value1/1 will supply a value of that type.  I can't come
up with the proof off the top of my head (it was a good night) but
I suspect that any implementation satisfying this declaration will
not be well typed.  So this declaration doesn't make much sense.

The second one remains the same,

:- some [T] pred give_me_a_value2(T::out).

and says that give_me_a_value2/1 will provide a value of *some* type, but
it won't tell you what that type is.  So you (the caller) have to be
prepared
to handle *any* type that comes back.  Basically, it's a constraint that the
caller be polymorphic rather than the callee.

The classic idiom is

:- some [T] make_a_foo(T::out) => foo(T).

which will provide *something* that has the property foo - there may be
many such candidates (e.g. subclasses).

> Pardon my clumsy attempts to understand this topic, but 
> *cough* thanks to
> C++ I'm not used to things having polymorphic return types :(

You probably are!  C++ just doesn't make it explicit.  For example,
you might have an array of foo objects.  You can assign any subclass
instance of a foo to an element of this array.  When you project out
an element of the array you don't know whether you're getting a plain
foo or a subclass instance - all you know is that whatever you get will
have the properties of a foo.  That's all that the existential typing
and typeclass constraint is saying.

> One final question, which of the following is appropriate:
> 
> :- pred transform1(T::in, T::out) <= object(T).

This says that transform1/2 works on any T that satisfies object(T) (i.e.
is a member of the object/1 typeclass) and produces another thing of the
same type.  I think this is probably what you're after.

> :- pred transform2(T::in, T::out) => object(T).

>From the direction of the arrow this is implicitly existentially quantified
and, AFAIK, you need to make that explicit:

:- some [T] pred transform2(T::in, T::out) => object(T).

This doesn't make much sense to me.  What it says is that there is/are
*some*
T that are members of the object/1 typeclass for which this predicate is
defined.  You can't know ahead of time what that type is, so you can't
construct a value for the input argument.  Hence you could never write
correctly typed code that could call transform2/2.

> :- pred transform3(T::in, T::out) <= object(T) => object(T).

Again, you need to make the existential quantification explicit and then
the interpretation depends upon how you bracket things.



Here are the rules of thumb I use for type quantification.

(a) I want to `label' objects so that `keys' for one instance aren't used
to access another instance:

:- type store(T).			% c.f. store module.

	% Every time I call new_store/1, I'm magicking up a new type
	% for it that distinguishes this store from all the others.
:- some [T] pred new_store(store(T)::uo) is det.

	% I can generate `keys' that can only be used on a particular store.
:- type mutvar(T1, T2).

	% All operations on mutvar/2s will be carried out in association
with
	% the store/1 they belong to.  Thanks to existential typing, we
can't
	% use a mutvar from one store with another store since the T2s
wouldn't
	% agree.
:- pred new_mutvar(T1::in, mutvar(T1, T2)::out, store(T2)::di,
store(T2)::uo) is det.

(b) I just want a standard polymorphic predicate - I don't care what the
argument type is:

:- pred list__length(list(T)::in, int::out) is det.

(c) I want a predicate that operates on anything that implements foo
behaviour:

:- pred fooalise(T::in, ...) <= foo(T).	% Any colour you like as long as
it's foo.

I can require that they be both foo and bar:

:- pred foobaralise(T::in, ...) <= (foo(T), bar(T)).

(d) I have a predicate that is going to return an object of some type
that will implement foo, but other than that I want to keep the details
a secret:

:- some [T] make_a_foo(T::out) => foo(T).

<and I can do the same sort of thing as before if it's also going to be a
bar>

(e) If I want collections of things that are different types but all of
which
are foo, then I have to do something like this:

	% If I deconstruct an any_foo/1, all I know is that what I get out
	% will be a member of the foo typeclass - other than that, all bets
	% are off.
:- type any_foo ---> some [T] any_foo(T) => foo(T).
:- type foo_list == list(any_foo).

	% Adding a new item to my list...
	Xs1 = ['new any_foo'(AFoo) | Xs],
	% And adding another of a different type, but a member of foo...
	Xs2 = ['new any_foo'(ADifferentFoo) | Xs1],
	% Taking a member out...
	Xs2 = [any_foo(Thing) | _],
	% All I can say here is that Thing is a member of foo and that's it.

Hope this helps,

Ralph
--------------------------------------------------------------------------
mercury-users mailing list
post:  mercury-users at cs.mu.oz.au
administrative address: owner-mercury-users at cs.mu.oz.au
unsubscribe: Address: mercury-users-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-users-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the users mailing list