www.digitalmars.com         C & C++   DMDScript  

D - Constrained Genericity (template suggestion)

reply reiter nomadics.com (Mac Reiter) writes:
One of the things I really like about the genericity/template system
in Eiffel is so-called "Constrained Genericity".  For the full
description, you can probably find information at www.eiffel.com or in
"Object Oriented Software Construction, 2nd Edition" (otherwise known
as OOSC2), but an extremely short example, converted to C++'ish syntax
would be:

template <typename T:SORTABLE> T max(T a, T b)
{
	return a<b ? b : a;
}

The only non-C++ code I meant to be present in the preceding code is
the use of "typename T:SORTABLE" instead of just "typename T".  (If I
got anything else wrong, it is just a typo, and not significant)  This
mechanism can also be applied to template classes (which is the
primary use of it in Eiffel), but the max() example is shorter and
simpler.  

What this means is that this template can only be instantiated with
types that are SORTABLE or are derived from SORTABLE.  Since max()
uses operator< on type T, we need to know that type T supports
operator<.  C++ does this at the point of call, which is OK as far as
it goes, but the above syntax has a few advantages:

1. What a type IS is more than just the collection of available
operators.  In the example, SORTABLE specifically means that operator<
provides a total ordering (I can't remember strong/weak definitions,
so I leave them out of this).  Other types may have operator< that
doesn't provide a total ordering, and would not derive from SORTABLE,
and would not be appropriate for use here, even though C++ would
compile it.

2. Because the constraints appear in the declaration of the template,
users of max() know that they can only use it on types that are
SORTABLE.  Self-documenting code is good...

3. Because the relevant interface is in the declaration, it *should*
be easier for compilers to provide more direct and meaningful error
messages.  I prefer "Error : templatized type T in max() must be of
type SORTABLE." over "Error : operator< is not defined for type
FLEEM".  This may just be a personal style kind of thing.

I seem to remember that you can use the "like" keyword to introduce
covariance constraints, as well.  For example, the previous template
could also be written as:

template <typename T1:SORTABLE, T2:like T1> T1 max(T1 a, T2 b)
{
	return a<b ? b : a;
}

The primary addition here is the second parameterized/templatized
type, which is also constrained, but is constrained to be "like" the
first type (T1).  This means that T2 can be the same as T1, or it can
be any type derived from T1 (I think -- sorry, my Eiffel is a little
rusty).  This is not especially meaningful for max(), but it can be
very useful for properly expressing constraints on generic classes
that need to manipulate different types, but need those types to have
specific relationships to each other.

I gave the above template form because Eiffel uses the "like" keyword,
but it looks unnecessary (and is, in that context).  After all, the
declaration could just be:

template <typename T1:SORTABLE, T2:T1> T1 max(T1 a, T2 b)

and it would mean exactly the same thing (T2 is of type T1 or is
derived from T1).  The "like" keyword is generally used inside
templatized classes to add members that are constrained to be "like"
one of the templatized types.

I have wandered, and I need to go read OOSC2 again, but the central
point (for those of you with the patience to put up with me this far)
is that Constrained Genericity and Type Covariance are useful
constructs that do not exist in C++ (without some painful twisting
around) and that I would like to see added to D.

Thank you for your time and consideration,
Mac Reiter
Software Engineer
Jan 22 2002
parent "OddesE" <OddesE_XYZ hotmail.com> writes:
"Mac Reiter" <reiter nomadics.com> wrote in message
news:3c4d7a82.405408206 news.digitalmars.com...
 One of the things I really like about the genericity/template system
 in Eiffel is so-called "Constrained Genericity".  For the full
 description, you can probably find information at www.eiffel.com or in
 "Object Oriented Software Construction, 2nd Edition" (otherwise known
 as OOSC2), but an extremely short example, converted to C++'ish syntax
 would be:

 template <typename T:SORTABLE> T max(T a, T b)
 {
 return a<b ? b : a;
 }

 The only non-C++ code I meant to be present in the preceding code is
 the use of "typename T:SORTABLE" instead of just "typename T".  (If I
 got anything else wrong, it is just a typo, and not significant)  This
 mechanism can also be applied to template classes (which is the
 primary use of it in Eiffel), but the max() example is shorter and
 simpler.

 What this means is that this template can only be instantiated with
 types that are SORTABLE or are derived from SORTABLE.  Since max()
 uses operator< on type T, we need to know that type T supports
 operator<.  C++ does this at the point of call, which is OK as far as
 it goes, but the above syntax has a few advantages:

 1. What a type IS is more than just the collection of available
 operators.  In the example, SORTABLE specifically means that operator<
 provides a total ordering (I can't remember strong/weak definitions,
 so I leave them out of this).  Other types may have operator< that
 doesn't provide a total ordering, and would not derive from SORTABLE,
 and would not be appropriate for use here, even though C++ would
 compile it.

 2. Because the constraints appear in the declaration of the template,
 users of max() know that they can only use it on types that are
 SORTABLE.  Self-documenting code is good...

 3. Because the relevant interface is in the declaration, it *should*
 be easier for compilers to provide more direct and meaningful error
 messages.  I prefer "Error : templatized type T in max() must be of
 type SORTABLE." over "Error : operator< is not defined for type
 FLEEM".  This may just be a personal style kind of thing.

 I seem to remember that you can use the "like" keyword to introduce
 covariance constraints, as well.  For example, the previous template
 could also be written as:

 template <typename T1:SORTABLE, T2:like T1> T1 max(T1 a, T2 b)
 {
 return a<b ? b : a;
 }

 The primary addition here is the second parameterized/templatized
 type, which is also constrained, but is constrained to be "like" the
 first type (T1).  This means that T2 can be the same as T1, or it can
 be any type derived from T1 (I think -- sorry, my Eiffel is a little
 rusty).  This is not especially meaningful for max(), but it can be
 very useful for properly expressing constraints on generic classes
 that need to manipulate different types, but need those types to have
 specific relationships to each other.

 I gave the above template form because Eiffel uses the "like" keyword,
 but it looks unnecessary (and is, in that context).  After all, the
 declaration could just be:

 template <typename T1:SORTABLE, T2:T1> T1 max(T1 a, T2 b)

 and it would mean exactly the same thing (T2 is of type T1 or is
 derived from T1).  The "like" keyword is generally used inside
 templatized classes to add members that are constrained to be "like"
 one of the templatized types.

 I have wandered, and I need to go read OOSC2 again, but the central
 point (for those of you with the patience to put up with me this far)
 is that Constrained Genericity and Type Covariance are useful
 constructs that do not exist in C++ (without some painful twisting
 around) and that I would like to see added to D.

 Thank you for your time and consideration,
 Mac Reiter
 Software Engineer

I was thinking of something else that looks a lot like what you are saying, but concerns operator oveloading. I'm gonna start a new thread about it as to not go too much off topic on yours, but what you are saying sounds very good to me. Read the new thread: Operator overloading: A way to make everybody happy? I'm going to point people from there to here, because I think these two subjects are related. -- Stijn OddesE_XYZ hotmail.com http://OddesE.cjb.net __________________________________________ Remove _XYZ from my address when replying by mail
Jan 22 2002