Dominik Honnef

My thoughts on the Go Generics Draft

Published:
Last modified:
by

The following are my thoughts on the Go Generics Draft. I will focus on the things that I disagree with or think are missing.

Contracts are nice, but where is the operator overloading?

To me, the generics problem has always also been a problem of operator overloading. Consider the basic example of a Sum function:

contract Addable(t T) {
  t + t
}

func Sum(type T Addable)(x []T) T {
  var total T
  for _, v := range x {
    total += v
  }
  return total
}

This function would be able to sum any slice of basic numeric types. It would, however, not be able to sum a slice of type int128 struct { l, h uint64 }. We could write a similar generic function that requires an Add method, but that demonstrates the problem: without operator overloading, generic functions can belong to one of two classes: those that work on basic types, and those that work on complex types.

This issue affects at least arithmetic, ordered types and containers. The draft has examples for each: Sum, sort.OrderedSlice and sets. Though at least the last one could be glossed over, as this has been an issue long before generics, due to map’s reliance on equality.

Of course, nothing in the draft prevents operator overloading from being implemented – from an implementation point of view, these two are orthogonal problems. However, Go has long refused to add operator overloading, Custom operators can easily lead to less readable code, after all.

An alternative would be the use of adaptors, but the draft only points out their absence.

My question is: does the Go team have any concrete plans for addressing this issue?

Restrictions on contract bodies

The draft says

The body of a contract may not refer to any name defined in the current package. This rule is intended to make it harder to accidentally change the meaning of a contract.

as well as

It is likely that this rule will have to be adjusted as we gain more experience with this design

I believe that this restriction will have to be lifted. One will almost certainly want to refer to types defined in the package. It may also prevent some interesting combinations of interfaces and contracts.

The generic dilemma

The design document claims to address the generic dilemma by providing the flexibility to implement generics in different ways, turning it into a compiler optimization problem.

But that’s not addressing the dilemma. It’s allowing someone to address it one day (hopefully), but it doesn’t propose an actual implementation that is different from C++-style compile-time specialization or Java-style boxing.

As such, it seems a bit weird that we’re now okay with implementing generics using one of those methods, when previously they were deemed “not good enough”.

Generics vs interfaces

The draft does not go into a lot of detail on the interactions between generics and interfaces. In particular, it avoids asking the question of when to use generics vs when to use interfaces. We may be able to look at other languages that feature both, but I believe that Go interfaces are sufficiently different to require independent decisions.

In particular it will be interesting to see when people will choose []T over []I or vice versa.