Go is a programming language with few syntactic elements and a simple design, which often means less expressiveness and more time for engineers to write repetitive logic. There has been a lot of community discussion and outcry about generics, and here are some of the discussions and feedback related to generics.

Many people believe that Go will never include generics, but this is not the correct conclusion, and it is likely that Go will include generics in its second major release.4 So the question that we will analyze in this article is - why Go has not had generics until now, and whether and how these reasons have been addressed. .

If you have a little knowledge of the Go language’s standard library, you can find some function signatures as follows.

1
2
3
4
5
6
package sort

func Float64s(a []float64)
func Strings(a []string)
func Ints(a []int)
...

The above functions are all provided by the sort package, and their functionality is very similar and the underlying implementation uses nearly the same logic, but they require multiple functions to be provided externally due to the difference in incoming types. Java’s generics solve this problem.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class ArraySortViaComparable {
    public <E extends Comparable> void insertionSort(E[] a) {
        for (int i = 1; i < a.length; i = i + 1) {
            Comparable itemToInsert = a[i];
            int j = i;
            while (j != 0 && greaterThan(a[j-1], itemToInsert)) {
                a[j] = a[j-1]
                j = j - 1
            };
            a[j] = itemToInsert;
        }
    }

    private static boolean greaterThan(E left, Object right) { return left.compareTo(right) == 1; }
}

This Java code implements generic array sorting logic using generic arrays as arguments. Any type that implements the Comparable interface, the insertionSort function can sort arrays composed of that object. Using generics reduces repetitive code and logic and provides engineers with greater expressiveness and thus efficiency.

Since generics can enhance the expressiveness of the language and improve the efficiency of engineers, why doesn’t Go support them so far? This article summarizes two reasons.

  • The generalization dilemma makes it necessary to choose between development efficiency, compilation speed, and runtime speed.
  • the current Go language solutions in the community are flawed, and the Go team does not feel that generic support is urgent enough.

Both of these reasons led to the Go language not including generics in version 1.x.

The Generic Dilemma

Generics, like other features, are not the only benefit, and adding them to a programming language presents a dilemma that requires a trade-off. Language designers need to weigh and choose between programming efficiency, compilation speed, and runtime speed, and the programming language has to choose to sacrifice one and keep the other two.

We use C, C++ and Java as examples of their different design considerations.

  • C is a system-level programming language that does not support generics and provides very limited abstraction capabilities on its own. This results in sacrificing programmer development efficiency, as is currently the case with the Go language, which requires manual implementation of the same logic for different types. But the benefits of not introducing generics are also obvious - reduced complexity for the compiler implementation, and guaranteed speed of compilation of the source code.
  • C++ is a completely different choice than C. It uses compile-time type specialization to implement generics, which provides very powerful abstraction capabilities. Although it improves the development efficiency of programmers by eliminating the need to write similar implementations of the same logic by hand, the compiler implementation becomes very complex and the large amount of duplicate code that can be generated by generic expansion can also lead to bloated and slow compilation of the final binary, and we often need linkers to solve the problem of duplicate code.
  • Java introduced generics in version 1.5, which are implemented with type erasure. Java’s generics are only used to check for correct types during compilation, and to ensure compatibility with older versions of the JVM, type erasure removes information about the generic type, making it unavailable at runtime. The compiler inserts additional type conversion instructions, and the boxing and unboxing of Java types reduces program execution efficiency compared to C and C++, which implement or generate code before runtime

When we are faced with the question of whether we should support generics, the question that actually needs to be considered is whether we should sacrifice development efficiency for engineers, compilation speed and greater compilation product or runtime speed.

The introduction of generics will definitely affect compilation speed and runtime, as well as increase the complexity of the compiler, so the community is also very careful when considering generics. the Go 2 generic proposal does not make a choice when faced with this issue, leaving it up to the specific implementation to decide whether it should affect compilation speed (compiling different type parameters separately) or runtime (using method calls to determine the specific function to execute at runtime).

No urgency, no refinement

The Go language team does not consider it urgent to include generics, but more important to improve the runtime mechanisms, including the scheduler, garbage collector and other features. The authors have no particular need for generics when using the Go language, except for having to use interface{} as a parameter to a method when providing some generic abstract logic, which is not a good practice, but one of the few ways to do so within the constraints of the current language.

Most of the generic proposals in the community have their own shortcomings and so will not be adopted by the Go team. Here we list some of them for you, and interested readers can visit the following links to learn more about them.

It is because adding generics to the Go language has not been a top priority for the team, and past proposals have had obvious flaws, that there has been no support for generics since the Go language was released over 10 years ago.

In late July 2019, the Go team released a draft of the Go 2 generic design Contracts - Draft Design, a design that The draft proposes to extend the Go language by adding argument polymorphism. With argument polymorphism, the arguments a function can take are no longer limited to subtyping, but can also have explicit structural constraint.

1
2
3
4
5
6
func Stringify(type T stringer)(s []T) (ret []string) {
	for _, v := range s {
		ret = append(ret, v.String()) // now valid
	}
	return ret
}

The proposal suggests how the Go language should support generics in terms of Syntax, Type constraint, Type inference, and Implementation.

  • Syntax – how are generics, functions, and methods declared and used?
  • Type constraints – How are type constraints defined?
  • Type derivation – When can function calls ignore type arguments?
  • Implementation – Does it use compile-time or run-time substitution?

Compared to previous proposals, this is the best the Go team can give so far, cmd/compile/internal/syntax: parse/print support for type parameters and contracts shows how the syntax in the proposal can be supported by modifying the compiler, however this is only a simple prototype and the final implementation and the draft itself will need to be discussed by the community.

Summary

The Go language has never flagged against adding generics to the language, a decision that many people have misunderstood. So far, the reasons for the Go language’s lack of generics can be summarized in two simple points.

  • the generalization dilemma is one that all programming languages need to face, and one that had to be thought through before adding it.
  • most of the current generic proposals have obvious flaws, and in version 1.x, improving other aspects of the language’s performance brings more benefits than generics do.

The Go 2 draft of generics does not address either of these issues for now. It only decided to introduce generics to enhance the expressiveness of the language and improve programmer productivity, but it bypassed the choice between compile speed and runtime speed, and it is unclear how the decision will be made. Dual implementation, etc.

The authors believe that the Go community will be able to make relatively reasonable decisions and solve the problems associated with the introduction of generics. In the end, let’s look at some of the more open-ended related issues, and the interested reader can think carefully about the following questions.

  • How does the design of generics in the Go draft differ from Java or other languages?
  • What standard libraries in the Go language can be overridden by generics?