DEV Community

Evan Lin
Evan Lin

Posted on • Originally published at evanlin.com on

[TIL] Golang Type Parameters: First Experience (Go2 Generics)

Preface:

Generics has always been a question that developers who have switched from C++ and Java to the Golang programming language often ask. This question is not only a major point of contention in language wars, but there are also many requests to add generics (reference). Here, I'll summarize and try out the latest version of Go2 to see how Generics is doing.

Thanks to the netizens who discussed on TG for providing the link. I tried out the Go2 Playground and looked at the changes in the Type Parameters proposal, and wrote down my thoughts. You can try it out and learn more about the content of the Type Parameters proposal together.

I get a strong urge to write code or read specs before a major event. The slides are clearly waiting for me.

Why Programming Languages Need Generics

Generics

Generic programming is a style or paradigm of programming languages. Generics allow programmers to use types that are specified later when writing code in strongly-typed programming languages, and specify these types as parameters during instantiation.
Enter fullscreen mode Exit fullscreen mode

(Source: wiki)

In other words, in strongly-typed programming languages, types must be given first. It's difficult to abstract types when writing functions. Allowing types to be applied later. Take a simple example, according to the article "Why Gnerics", it once gave this great example. Let's assume you need to sort all the elements in a slice from smallest to largest.

For Int, you might write:

func ReverseInts(s []int) []int {
    first := 0
    last := len(s) - 1
    for first < last {
        s[first], s[last] = s[last], s[first]
        first++
        last--
    }
    return s
}
Enter fullscreen mode Exit fullscreen mode

And for strings, it might be like this:

func ReverseInts(s []string) []sting {
    first := 0
    last := len(s) - 1
    for first < last {
        s[first], s[last] = s[last], s[first]
        first++
        last--
    }
    return s
}
Enter fullscreen mode Exit fullscreen mode

Based on the above methods, you will find that the two functions are really no different. But because of the different data formats, you need to write two functions separately. This is often not intuitive for maintenance. When you find that it can be optimized or there is a problem, you need to maintain all the code that uses the type at once, which is not intuitive at all.

Are there any other alternatives in Golang before Type Parameters?

Let's not talk about Generics. In fact, Golang can use Interfaces to do related development. Here are the related implementation methods. Let's first explain what interfaces are:

Interfaces are named collections of method signatures.
Enter fullscreen mode Exit fullscreen mode

(Refer: Go by Example: Interfaces)

According to this example, you can easily understand the main usage of interfaces. So what is the well-known application method? It's sort.Sort()

package main

import "sort"
import "fmt"

type ByLength []string

func (s ByLength) Len() int {
    return len(s)
}
func (s ByLength) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}
func (s ByLength) Less(i, j int) bool {
    return len(s[i]) < len(s[j])
}

func main() {
    fruits := []string{"peach", "banana", "kiwi"}
    sort.Sort(ByLength(fruits))
    fmt.Println(fruits)
}
Enter fullscreen mode Exit fullscreen mode

Through the sort.Sort() function, you can apply the sorting method of int or string, and you can even define your own Len(), Swap() and Less() for a custom struct, and then apply sort.Sort(). Of course, there are also Go generation, reflection, and other methods that can be used. I won't go into detail here. Everyone is welcome to refer to the extended reading.

Extended Reading

Golang Generic Proposal Introduction Video:

The following is a slide based on the Golang Generics Proposal. Please note that this is the first version (2019/07) of the Generic Proposal, which still uses Contracts to implement Type Parameters. But this part has been modified to interfaces to make the syntax clearer. I must be honest, when Contracts came out, I really didn't understand the difference between contracts and interfaces.

What happened to contracts?
An earlier draft design of generics implemented constraints using a new language construct called contracts. Type lists appeared only in contracts, rather than on interface types. However, many people had a hard time understanding the difference between contracts and interface types. It also turned out that contracts could be represented as a set of corresponding interfaces; there was no loss in expressive power without contracts. We decided to simplify the approach to use only interface types.
Enter fullscreen mode Exit fullscreen mode

https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md

Try out Go2 Playground

Let's use the Type Parameters provided by the Go2 playground to look back at our original problem. Is there a way to implement it through Type Parameters?

Let's see the result immediately: https://go2goplay.golang.org/p/doitUP4\_1Jm

func ReverseSlice[T any](s []T) []T {
    first := 0
    last := len(s) - 1
    for first < last {
        s[first], s[last] = s[last], s[first]
        first++
        last--
    }
    return s
}

func main() {
    fmt.Println(ReverseSlice([]string{"Hello", "playground"}))
    fmt.Println(ReverseSlice([]int{1, 3, 5}))
}
Enter fullscreen mode Exit fullscreen mode

Type Parameters with Restrictions

Using the any Type Parameter is actually quite convenient, but it often depends on the fact that the data you may be processing is not suitable for all types. In fact, you need to add some data restrictions. Let's illustrate with an example:

Example: Comparing the larger of two parameters

    if a < b {
        return a
    }
    return b
Enter fullscreen mode Exit fullscreen mode

This is a very simple comparison method, but you can see that if you write this method through Type Parameters. You will find that the input parameters will not support string, so you need the following related modifications.

https://go2goplay.golang.org/p/9BgTT0hCgD7

type numeric interface {
    type int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64
}

func min[T numeric](a, b T) T {
    if a < b {
        return a
    }
    return b
}

func main() {
    fmt.Println(min(42, 84))
    fmt.Println(min(3.14159, 2.7182))
}
Enter fullscreen mode Exit fullscreen mode

Small Conclusion:

Type Parameters and Go2 Playground are still under development. In addition to being not so intuitive and easy to use, there may be some bugs. But I'm really looking forward to using Type Parameters to truly decouple algorithms and types in development, making functions more concise.

Related Articles:

-

Golang Blog: 2019/07/31 “Why Gnerics

-

Golang Blog: https://blog.golang.org/generics-next-step

-

Type Parameters in Go https://go.googlesource.com/proposal/+/master/design/15292/2013-12-type-params.md

-

https://groups.google.com/g/golang-dev/c/U7eW9i0cqmo/m/-gDfa\_6KAAAJ?fbclid=IwAR27mCQ8vgV9w8A201SlLMkyTnWJbfKVBoVRFutGU1zt1\_KOCib9pVeQSMs

-

Type Parameters Draft Design in Gohttps://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md

-

Go2 Playground https://go2goplay.golang.org

Top comments (0)