I like the second draft for generics. It seems to me a large
simplification and improvement over the first draft. Considering just
the state of Go today, I would be quite happy with this, even if it's
not perfect. Thanks to Ian, Robert, and everyone else for their work
on this.

Also, I would vote for square brackets over parentheses.

But I do have concerns related to the future development of Go. In
particular, if we think it likely that a future version of Go will
allow operator overloading, then perhaps type lists are not the best
choice.

To my mind, the biggest defect in the design draft is that we can't
write generic functions and types that work transparently with both
builtin and user-defined types (that do not inherit appropriate
behavior from an underlying builtin type). For example, we can't write
a

func Min[type T ...](a, b T) T { ... }

that works both when T is int and when T is

type BigInt struct { i *big.Int }

Instead, we would use workarounds such as writing two versions of Min,
or passing in an adaptor function or object; in the case of Min, a
comparison function. And that's OK, especially in an initial version
of generics.

But generics would be significantly easier to use if we could write
functions that work on both builtin and user-defined types. The two
most likely candidates for allowing this seem to be operator
overloading (where BigInt might have a method named "<", "operator<",
or some such, that allows it to be used with the < operator) and
methods on builtin types (where int might be given a method named Less
with the same behavior as the < operator). Of course, other solutions
could be imagined, but I'll confine my speculations to those two.

Now let's try to imagine how sorting slices might be implemented in
the standard library in various futures.  Of course, the current sort
package would have to be kept and maintained for a long time.

If Go2 implements the current draft with type lists, then we might add
a sort2 package containing something to:

func SliceBy[type T](s []T, less(T, T) bool) { ... }

type Ordered interface {   // Copied from the draft
    type int, int8, int16, int32, int64,
    uint, uint8, uint16, uint32, uint64, uintptr,
    float32, float64,
    string
}

func Slice(type T Ordered)(s []T) {
    SliceBy(s, func(a, b T) bool { return a < b })
}

type Lesser[type T] interface { Less(T) bool }

func SliceByLess(type T Lesser[T])(s []T) {
    SliceBy(s, T.Less)
}

All well and good. Now say time goes by and Go3 adds operator methods.
Nothing in the sort2 package expresses a unified sort using operator
methods, so we need a new package sort3:

func SliceBy[type T](s []T, less(T, T) bool) { ... }

type Lesser[type T] interface { <(T) bool }   // Or whatever the syntax is.

func Slice(type T Lesser)(s []T) {
    SliceBy(s, func(a, b T) bool { return a < b })
}

(We might just add sort3.Lesser and sort3.Slice into the sort2 package
under different names, but I suspect the aim would be to eventually
deprecate sort2.)

The effects will ripple through other code, both in and outside the
standard library. Suppose some Go2 code has a chain of generic
functions A calls B calls C calls D, where each exists in two
versions, one for builtin types and one for user-defined types, and
the two versions of D call sort2.Slice or sort2.SliceByLess. When Go3
with operator methods arrives, if we want to unify these, we have to
write a third version of each of A, B, C, and D, where D calls
sort3.Slice.

On the other hand, suppose Go2 has type lists and Go3 gives builtin
types methods corresponding to operators. Assuming the name Less is
used for <, sort2.SliceByLess now handles both builtin and
user-defined types, so we don't need a sort3 package. And in the ABCD
scenario, we can just keep the SliceByLess version of each, and
quietly let the sort2.Slice versions vanish as they become unused.

[Important point===>] This means that if Go2 has type lists in
interfaces, there will be a strong incentive for Go3 to give builtin
types methods, even if we think that operator overloading is otherwise
a superior solution, because operator overloading will require much
more new code to be written.

Instead of using type lists, suppose Go2 allowed interfaces to require
the presence of specific operators. Then the sort2 header might look
like this:

func SliceBy[type T](s []T, less(T, T) bool) { ... }

type Lesser[type T] interface { <(T) bool }

func Slice(type T Lesser)(s []T) {
    SliceBy(s, func(a, b T) bool { return a < b })
}

type MLesser[type T] interface { Less(T) bool }

func SliceM(type T MLesser[T])(s []T) {
    SliceBy(s, T.Less)
}

(Note that the first part is identical to the previous sort3 header.
But in Go2 we also need MLesser and SliceM in order to handle
user-defined types.)

This leaves us more easy options in Go3. If Go3 implements operator
overloading, then sort2.Slice now handles both builtin and
user-defined types, and code using sort2.SliceM can be allowed to
wither away. If Go3 implements an int.Less method, then sort2.SliceM
is now the good version, and code using sort2.Slice can be allowed to
wither away as it becomes unused.

So maybe the alternative of allowing interfaces to require specific
operators deserves another look, to see if it's really not viable in
Go2. I would suggest that perhaps the initial version of generics need
not support all operators; maybe it's enough to support only those
that apply to numeric types (string should be accepted by interfaces
requiring +, <, and other comparison operators). Or maybe a slightly
larger subset of operators would do.

As a final note - this post, like all speculations about the future,
is rather fuzzy. I realize that. Nevertheless, I think it is important
to realize that the choices we make now carry consequences for our
options after a few years' time.

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAADvV_stjEvD4pLwGNmahAEzc0UDNLuzwttZ9wDg78BOJfDA5Q%40mail.gmail.com.

Reply via email to