dep-generic-methods icon indicating copy to clipboard operation
dep-generic-methods copied to clipboard

Subtype relation requires same number of type parameters. Can that be improved?

Open lrhn opened this issue 10 years ago • 4 comments

The subtyping relation between generic functions require them to have the same number of generic parameters. For classes that is not always the case. For example:

   class IdentityTranform<T> implements Transformation<T, T> { ... }

Since you "invoke" each type individually, there is no problem. Instance methods can override the superclass version, and we allow a different number of parameters, as long as its compatible (by some of the parameters being optional).

Would we want a sub-class method to allow more type parameters, or to accept fewer? That is, should we have optional type parameters? Maybe something like:

     T foo<T, S>(S value, T convert(S value)) ....

being overridden by

    T foo<T, S, [R = S]>(S value, T convert1(S value), [T convert(R value), R otherValue]) ....

Optional type parameters would be written as [T = defaultType, ...] where the default-type can be any type, including earlier parameters of the same function.

Does this make any sense?

lrhn avatar Mar 19 '15 15:03 lrhn

How to only pass the 3rd type argument but not the 2nd (in a list of 3+), would you pass dynamic for the 2nd or would this need named optional type arguments, and would it then still take the specified default type for the 2nd? :-/

zoechi avatar Mar 19 '15 15:03 zoechi

The comparison to class inheritance is a bit of a red herring I think - there is no subtyping between generic classes in Dart, only generic class instantiations. This isn't really observable since any use of a class is either as an explicit instantiation (List<T>) or an implicit instantiation (List), but nonetheless...

That aside, yes, there are things we could do with this that could be interesting. If we relaxed the semantics on arity mismatches on type argument lists so that passing too few parameters filled in the rest with dynamic, and/or passing too many dropped the extra, then we could usefully allow things with different number of type arguments to be subtypes. I don't really like this approach - it doesn't match up with how generic classes behave (where if you get the arity wrong there you get all dynamic) - and it seems kind of error prone. It's not totally unreasonable though.

Adding optional type arguments with a default is something that I think probably work out fairly nicely though. @jakemac53 brought this up in conversation yesterday as something he would be interested in. Your example above is a good one - adding optional arguments is a valid extension, and you might want an additional type argument to describe the type of the optional arguments.

It would probably be useful to provide syntax at the call side to use the default argument at a specific position: e.g. foo<int, _, String> could mean instantiate foo with int, the default for the second position (dynamic if none?) and String.

It would be nice if calling a generic without any type arguments at all used the defaults instead of dynamic. foo(a, b) means foo<_, _, _>(a, b). This might be an argument for adding this to the proposal now, to avoid having a quasi-breaking change later. Alternatively there could be syntax for this: e.g. foo<>(a, b) means use all of the default parameters, or perhaps foo<_>(a, b)?

I had actually considered adding something like this to the generic methods proposal, but felt that it would be nice to add this uniformly to both generic classes and generic methods, and so was considering that it might be useful to do this as a separate DEP. But I'm open to making it part of this DEP - either only for generic methods, or for both.

Named type arguments could also probably be made to work out. My initial reaction is to say that perhaps that's more mechanism then we really need, but if there are good use scenarios then we could consider it.

leafpetersen avatar Mar 19 '15 19:03 leafpetersen

optional args reminded me of class Point<T extends num> where folks pointed out that defaulting T to dynamic isn't very useful. So you almost want to have something more like class Point<T extends num = num> or interpret it as that.

jmesserly avatar Mar 20 '15 17:03 jmesserly

I agree that the comparison with class type parameters doesn't really give anything - the real comparison is with instance method parameters where the corresponding method in a subclass can have more optional parameters. Since generic methods will be part of the class signature, subclasses will need to be compatible with super-classes, but having some wiggle room is usually a good idea.

And it might be better served in another DEP, since adding optional type parameters should also apply to classes.

lrhn avatar Mar 21 '15 15:03 lrhn