为什么Go语言中没有泛型?

huangapple go评论108阅读模式
英文:

Why are there no generics in Go?

问题

有人知道为什么Go语言中没有对泛型/模板/名字等特性的真正支持吗?虽然有一个通用的map,但它是由编译器提供的,而Go程序员无法编写自己的实现。在讨论使Go尽可能正交的同时,为什么我可以使用通用类型,却无法创建新的通用类型呢?

特别是在函数式编程方面,有lambda甚至闭包,但由于静态类型系统缺乏泛型,我该如何编写像filter(predicate, list)这样的通用高阶函数呢?好吧,可以使用interface{}来实现链表等,但会牺牲类型安全性。

看起来泛型只会作为事后添加到Go中,如果有的话。我相信Ken Thompson会做得比Java的实现者更好,但为什么要将泛型排除在外呢?或者它们已经计划好了,只是还没有实现呢?

英文:

Does anybody know why there is no real support for generics/templates/whatsInAName in Go? So there is a generic map, but that's supplied by the compiler, while a Go programmer can't write her own implementation. With all the talk about making Go as orthogonal as possible, why can I USE a generic type but not CREATE a new one?

Especially when it comes to functional programming, there are lambdas, even closures, but with a static type system lacking generics, how do I write, well, generic higher order functions like filter(predicate, list)? OK, Linked lists and the like can be done with interface{} sacrificing type safety.

It looks like generics will only be added to Go as an afterthought, if at all,. I do trust Ken Thompson to do way better than the Java implementers, but why keep generics out? Or are they planned and just not implemented yet?

答案1

得分: 82

<sup>注意:泛型在Go的1.18版本中被添加。</sup>


你可以在这里找到答案:http://golang.org/doc/faq#generics

> 为什么Go没有泛型类型?
>
> 泛型可能在某个时候被添加。我们并不感到迫切需要它们,尽管我们理解一些程序员的需求。
>
> 泛型很方便,但它们会增加类型系统和运行时的复杂性。我们还没有找到一个设计,能够以复杂性相称的价值,尽管我们一直在思考。与此同时,Go内置的映射和切片,以及使用空接口构建容器(需要显式拆箱)的能力,意味着在许多情况下,可以编写能够实现泛型功能的代码,尽管不够流畅。
>
> 这仍然是一个未解决的问题。

英文:

<sup>Note: Generics were added to Go in version 1.18.</sup>


You will find the answer here: http://golang.org/doc/faq#generics

> Why does Go not have generic types?
>
> Generics may well be added at some point. We don't feel an urgency for them, although we understand some programmers do.
>
> Generics are convenient but they come at a cost in complexity in the type system and run-time. We haven't yet found a design that gives value proportionate to the complexity, although we continue to think about it. Meanwhile, Go's built-in maps and slices, plus the ability to use the empty interface to construct containers (with explicit unboxing) mean in many cases it is possible to write code that does what generics would enable, if less smoothly.
>
> This remains an open issue.

答案2

得分: 37

Go 1.18版本中实现了泛型的提案,该版本于2022年3月15日发布(https://go.dev/blog/go1.18)。

"Go 2"

泛型设计最初在Go2的框架下开始,首先在https://blog.golang.org/go2draft上发布了几个草案规范,接下来的三年里还有几个草案规范。

Go 1

Go语言的资深开发者之一Russ Cox在一篇名为《泛型困境》的博文中提出了以下问题:

> …你想要慢速的程序员、慢速的编译器和臃肿的二进制文件,还是慢速的执行时间?

没有泛型会导致程序员编写速度慢,C++风格的泛型会导致编译器变慢,而Java的装箱拆箱方法会导致执行时间变慢。

博文中没有提到的第四种可能是采用C#的方法。在需要时在运行时生成特定的代码,我非常喜欢这种方法,但是Go与C#非常不同,所以这种方法可能根本不适用...

我应该提到,使用流行的Java 1.4技术中的泛型编程方法(将类型转换为interface{})会遇到与装箱拆箱相同的问题(因为我们正在进行装箱拆箱操作),除了丧失编译时类型安全性之外。对于小类型(如int),Go会优化interface{}类型,使得被转换为interface{}的int列表占用连续的内存区域,仅占用正常int的两倍空间。但是,从interface{}进行类型转换仍然需要运行时检查的开销。参考资料

所有为Go添加泛型支持的项目(有几个,并且都很有趣)都统一采用了C++的编译时代码生成方法。

英文:

A proposal for generics was implemented for the Go 1.18 release, which was released on 15 March 2022

"Go 2"

The generics design started under the umbrella of Go2, first at https://blog.golang.org/go2draft and there were several more draft specs over the next 3 years.

Go 1

Russ Cox, one of the Go veterans wrote a blog post entitled The Generic Dilemma, in which he asks

> …do you want slow programmers, slow compilers and bloated binaries, or slow execution times?

Slow programmers being the result of no generics, slow compilers are caused by C++ like generics and slow execution times stem from the boxing-unboxing approach that Java uses.

The fourth possibility not mentioned in the blog is going the C# route. Generating the specialized code like in C++, but at runtime when it is needed. I really like it, but Go is very unlike C# so this is probably not applicable at all…

I should mention that using the popular Java 1.4 like technique of generic programming in go that casts to interface{} suffers from exactly the same problems as boxing-unboxing (because that's what we are doing), besides the loss of compile time type safety. For small types (like ints) Go optimizes the interface{} type so that a list of ints that were cast to interface{} occupies a contiguous area of memory and takes only twice as much space as normal ints. There is still the overhead of runtime checks while casting from interface{}, though. Reference.

All projects that add generic support to go (there is several of them and all are interesting) uniformly go the C++ route of compile time code generation.

答案3

得分: 4

为了补充和更新@Vinzenz和@user7610的出色答案。

> 虽然还不确定,但经过十多年的工作,泛型(俗称为generics)的设计似乎即将在未来一两年内推出。在现有语言中找到一个既能正常工作又能融入其中的设计是一个非常困难的问题,但Ian Taylor在这个问题上投入了大量的精力,现在看来答案已经近在眼前。请参阅https://evrone.com/rob-pike-interview。

《类型参数-草案设计》支持使用类型参数,您可以阅读处理传入参数的函数,而不依赖于函数声明中指定的类型。请参阅https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md。

例如,PrintSlice函数接收一个整数或字符串的切片并打印它。请参阅https://www.jetbrains.com/help/go/how-to-use-type-parameters-for-generic-programming.html。

package main

import "fmt"

func PrintSlice(type T)(s []T) {
	for _, v := range s {

		fmt.Print(v)
	}
}

func main() {
	PrintSlice([]int{1, 2, 3, 4, 5, 6, 7, 8, 9})
	PrintSlice([]string{"a", "b", "c", "d"})
}

您可以在https://go2goplay.golang.org/p/I09qwKNjxoq上测试此示例。这个playground的工作方式与通常的Go playground相同,但它支持泛型代码。请参阅https://blog.golang.org/generics-next-step。

泛型多态基本上意味着“这个函数或数据结构对任何类型都能正常工作”。这就是我们所说的泛型。例如,数组的长度不取决于数组中的元素是什么。请参阅https://news.ycombinator.com/item?id=23560798。

泛型可能被添加到Go的最早版本是Go 1.17,计划于2021年8月发布。请参阅https://blog.golang.org/generics-next-step。

英文:

To add to and update the excellent answers by @Vinzenz and @user7610.

> Although it's far from certain, after over a decade of work it looks
> like a design for parametric polymorphism, what is colloquially but
> misleadingly called generics, is coming in the next year or two. It
> was a very hard problem to find a design that works within the
> existing language and feels as if it belongs, but Ian Taylor invested
> a phenomenal amount of energy into the problem and it looks like the
> answer is now in reach. See https://evrone.com/rob-pike-interview.

"Type Parameters - Draft Design" supports the use of type parameters where you can read functions that handle incoming parameters without depending on the type specified in the function declaration. See https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md.

For example, the PrintSlice function receives a slice of integers or strings and prints it. See https://www.jetbrains.com/help/go/how-to-use-type-parameters-for-generic-programming.html.

package main

import &quot;fmt&quot;

func PrintSlice(type T)(s []T) {
	for _, v := range s {

		fmt.Print(v)
	}
}

func main() {
	PrintSlice([]int{1, 2, 3, 4, 5, 6, 7, 8, 9})
	PrintSlice([]string{&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;})
}

You can test this example here https://go2goplay.golang.org/p/I09qwKNjxoq. This playground works just like the usual Go playground, but it supports generic code. See https://blog.golang.org/generics-next-step.

Parametric polymorphism basically means 'this function or data structure works identically with any type". That's what we also call generics. Eg the length of an array doesn't depend on what's in the array. See https://news.ycombinator.com/item?id=23560798.

The earliest that generics could be added to Go would be the Go 1.17 release, scheduled for August 2021. See https://blog.golang.org/generics-next-step.

答案4

得分: 2

根据这篇帖子的实际情况:

> 许多人错误地得出结论,即Go团队的立场是“Go永远不会有泛型”。相反,我们理解泛型的潜力,既可以使Go变得更加灵活和强大,也可以使Go变得更加复杂。如果我们要添加泛型,我们希望以尽可能少的复杂性获得尽可能多的灵活性和功能。

英文:

Actually, according to this post:

> Many people have concluded (incorrectly) that the Go team’s position is “Go will never have generics.” On the contrary, we understand the potential generics have, both to make Go far more flexible and powerful and to make Go far more complicated. If we are to add generics, we want to do it in a way that gets as much flexibility and power with as little added complexity as possible.

答案5

得分: 0

参数化多态(泛型)正在考虑用于Go 2。

这种方法将引入一个“约束”(contract)的概念,用于表达对类型参数的约束:

contract Addable(a T) {
  a + a // 也可以是 +=
}

然后可以这样使用该约束:

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

这只是一个提案阶段。


你的filter(predicate, list)函数可以使用类型参数来实现,如下所示:

func Filter(type T)(f func(T) bool, l []T) (result []T) {
  for _, e := range l {
    if f(e) {
      result = append(result, e)
    }
  }
  return result
}

在这种情况下,没有必要对T进行约束。

英文:

Parametric polymorphism (generics) is under consideration for Go 2.

This approach would introduce the concept of a contract, that can be used to express constraints on type parameters:

contract Addable(a T) {
  a + a // Could be += also
}

Such a contract could then be used thusly:

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

This is a proposal at this stage.


Your filter(predicate, list) function could be implemented with a type parameter like this:

func Filter(type T)(f func(T) bool, l []T) (result []T) {
  for _, e := range l {
    if f(e) {
      result = append(result, e)
    }
  }
  return result
}

In this case, there is no need to constrain T.

huangapple
  • 本文由 发表于 2010年10月12日 14:10:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/3912089.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定