Type parameters

Graydon Hoare graydon at mozilla.com
Tue Sep 5 13:49:16 PDT 2006

Nicolas Cannasse wrote:

> There are a lot of useful cases. Basically when you don't have any
> contraints on the type parameter, all you can do is manipulate it
> abstractly. It works well for all kind of data structures and
> containers, but fails to express reusable components.

This is a sort of general comment. The point I'd like to address is a 
sort of mental arithmetic, or weighting of features and comparison with 
existing forms of safety and expressiveness. Add up these things in your 

   - Complexity of implementing the feature
   - Complexity of proving the feature correct
   - Complexity of explaining feature to users
   - Complexity of resulting "clever" API-code that users will have
     to understand in the wild (eg. note the difficulty many Java 5
     users have with the parametric bounds all over its standard

and compare them to some mental weight of:

   - Set of things you can express most conveniently with this
     feature, that you otherwise cannot express if this one
     feature is missing.

My feeling is that bounded type parameters just aren't worth the cost. 
That's why I avoided them in my proposal. Of course others might perform 
the "mental arithmetic" above with different weights and come to a 
different conclusion. But let's try to be concrete with examples.

> For instance in haXe you can do the following
> interface Required {
> ...
> }
> class Api< T : Required > {
> }
> That means that in the implementation of Api you can call any method
> defined in "Required". The type parameter is restricted to classes
> "implementing" Required.

Sure, but again this is a sort of vague example. In a lot of real 
examples, if you want an object of type "Required" that's actually *all* 
you need to know about the type; you don't need to name the subtype of 
"Required" because all you're going to do is call a few interface 
methods on it. In other words, in most real examples I've encountered 
the code:

   class foo< T : Bar>
     function bleh(t : T) { ... use some Bar methods ... }

where Bar is some interface, can often be written as:

   class foo
     function bleh(t : Bar) { ... use some Bar methods ... }

without type parameters *at all*.

The point I'd like you to focus on is whether there are any important 
*real* examples wherein you need *both* a type parameter *and* a bound 
on the parameter. I'd argue that there are very few.

One example everyone likes to give is a container with sorting; the idea 
is that once a type "implements Sortable" it can be put in a sorted 
associative map, or such, even while the map enforces type identity on 
the key and value types. But this is not a very good example, because 
it's actually a lot of work to express the constraint that two 
"Sortable" subtypes can sort with respect to their own subtype, but not 
necessarily with respect to one another. It's much *easier* in this case 
to present the following style of Map:

class Map.<K,V> {
   lessThan : func(K,K):Boolean;
   Map(lt : (func(K,K):Boolean)) : lessThan(lt) {}

This is the kind of thing I meant in my comments on the parametric type 
proposal: since we have first class functions, a lot of the "utility 
interfaces" that people often want to use parameter bounds for can just 
be passed along as auxiliary functions. And moreover that it's actually 
*easier* to express common patterns in, a lot of the time.

I'm happy to consider counterexamples, of course, but I'd like you to 
try holding them up to that criterion before assuming that "the feature 
can be done so it should be done". It's an expensive feature, 
complexity-wise. We want to keep complexity down. Take some time in an 
editor-buffer and work through your examples. See if they're really any 
more convenient with the bounded parameter vs. the parametric 
first-class function.


More information about the Es4-discuss mailing list