My Go Resolutions for 2017(from Russ cox's blog)

时间:2024-08-18 19:02:50

我的2017年Go决议

一年之季始于春,我认为写一些今年我希望在Go上做的东西是有意义的。

我每年的目标是帮助Go开发人员。我想确保我们在Go团队中所做的工作对Go开发者有重大的积极影响。可能听起来很明确,但是有各种常见的方面无法实现这一点:例如,花费太多的时间清理或优化不需要的代码;仅响应最常见或最近的抱怨或请求,或者重点关注短期改进。重要的是退后一步,确保我们将开发工作重点放在最好的地方。

这篇文章概述了我今年关注的几大重点。这只是我的个人清单,不是Go团队的清单。

发布这个的一个原因是收集反馈。如果这些引发了你自己的任何想法或建议,请随时在下面或链接的github问题上发表评论。

另一个原因是要明确我意识到这些问题很重要。我经常认为Go团队的缺乏行动是由于我们认为一切都是完美的,而不只是其他一些较高优先级的工作要先做。

类型别名

在大型代码库重构期间有个经常性问题:将类型从一个包移动到另一个包。去年我们试图用通用的别名来解决这个问题,没有成功的原因有两个:我们没有足够的解释这个变化,我们按时交付,所以Go 1.8中没有准备好别名。从这方面的经验学习,我做了一个谈话,写了一篇关于底层问题的文章,在Go issue tracker上展开了富有成效关于解决方案空间的讨论。看起来有限类型别名是正确的下一步,我想确保这些在Go 1.9中顺利落地。 #18130

包管理

我在2010年2月设计了Go支持下载已发布的软件包(“goinstall”,成为”go get”)。自那时以来发生了很多事情。特别是,其他语言生态系统真正提高了人们对包管理的期望,开源世界大多同意语义版本化,这为推断版本兼容性提供了有用的基础。Go需要在这里做得更好,一些贡献者一直在研究解决方案。我想确保这些想法被集成到标准的Go工具链中,并使包管理成为人们喜欢go的一个原因。

构建改进

在Go命令的构建系统的设计中有一些缺点,它们早就应该被解决。有三个代表性的例子,我打算通过一些重新设计go命令的内部结构来解决。

构建可能太慢,因为go命令不会像应用程序一样积极地缓存构建结果。许多人没有意识到,go install保存它的工作但go build没有,他们运行多次go build命令是慢的,因为后续的构建做了许多不必要的工作。当依赖被修改时,没有使用go test -i的重复go test命令有同样的问题。所有的构建应该尽可能增量。 #4719

测试结果也应该缓存:如果测试的输入都没有改变,那么通常没有必要重新运行测试。这将使得当没有或很少修改时运行”all tests”非常便宜。#11193

GOPATH之外的工作应该与GOPATH之内的工作几乎一样。特别是,应该可以git clone一个repo,切换到目录(cd into it),运行go命令,并使他们正常工作。软件包管理只会使这更重要:你需要能够处理不同版本的软件包(例如V1和V2),而不需要为他们提供完全独立的GOPATH #17271

代码库集

我认为这有助于我在准备关于代码库重构(见上面)的谈话和文章中有来自真实项目中的正确例子。我们还定义了vet添加必须针对实际程序中经常发生的问题。我想看到真实实践中的这种分析--检查对真实程序的影响和可能的改进--成为我们讨论和评估Go的变化的标准方法。

现在还没有一个约定的有代表性的代码库全集来用于这些分析:每个人必须首先创建自己的,这需要太多的工作。我想组成一个单一的,自包含的git repo,人们可以为这些分析检出包含我们的官方基准全集。可以从github上前100个按stars或forks或两者排名的go语言代码库开始。

自动vet

Go发布包包含这个强大的工具,go vet,可以指出正确性缺陷。检查的门槛很高,所以你应该听vet的话。但每个人都必须记住运行它。如果你不必记住会更好。特别是,我认为我可以在go tes的测试二进制文件的最终编译和链接期间并行运行vet,而不会使compile-edit-test的周期变慢。如果我们可以做到这一点,如果我们启用的vet检查限制在基本100%正确的子集,那么我们可以通过vet来完成一个运行测试的前提条件。那么开发人员不需要记得去运行go vet。他们运行go test,偶尔vet会报告一些重要的事情,可以避免调试会话。  #18084  #18085

Errors & best practices

Go中错误报告的预期契约的一部分是这些功能:包含有效的上下文,包含正在尝试的操作(例如函数名称及其参数)。例如,这个程序:

err:=os.Remove(“/tmp/nonexist”)

fmt.Println(err)

打印输出:

remove /tmp/nonexist: no such file or directory

没有太多的Go代码添加像os.Remove那样的上下文。太多代码只做了

if err != nil {

return err

}

整个调用堆栈都是这样子,丢弃了应该报告的有用上下文(如上面的 remove /tmp/nonexist),我想了解我们对包含上下文的期望是否错误,或者我们可以做些什么来使编写返回更好错误的代码更容易。

社区还有许多关于剥离错误上下文的商定接口的讨论。我想尝试了解什么时候有意义,以及我们是否应该采取官方建议。

Context & best practices

我们在Go 1.7中添加了新的context包,用于保持请求范围内的信息,如超时、取消状态和凭据。单个上下文是不可变的(像一个单独的string或int):只能导出一个新的,修改的上下文,并将该上下文明确地向下传递给调用堆栈,或者(不太常见)反向传给调用者。上下文现在通过诸如database/sql和net/http之类的API携带,主要是因为当调用者对结果不关心时可以停止处理请求。超时信息适合在上下文中携带,但是,使用我们移除的真实例子,数据库选项不是,因为他们不太可能同样地应用于所有可能的数据库操作请求。关于当前的clock source和logging sink呢?是否适合在上下文中存储?我想尝试理解和描述适合或不适合使用上下文的标准。

内存模型

Go的内存模型是有意低调的,与其他语言相比,对用户的承诺很少。事实上,它首先是阻止人们阅读文档的余下部分。同时,它对编译器的要求比其他语言多:特别是,一个整形值的竞争不足以让您的程序以任意方式表现异常。但也有一些完全差距,特别没有提到sync/atomic包。我认为核心的编译器和运行时开发者都同意这些 atomics包的行为应该与C++ seqcst atomics或java volatiles一样,当我们还是要在内存模型中仔细地写下来,也可能在一个长的博客。  #5045 #7948  #9442

Immutability

竞争检测器(race detector)是Go中最受欢迎的一个特性之一。但没有竞争(race)会更好。如果有一些合理的方式将参考不可变性(immutablility)整合到go,我会喜欢它,以便程序员可以清楚,检查断言关于什么可以或不可以写,从而在编译期消除竞争。Go已经有一个不变类型,string;追溯定义string为不可变[]byte的命名类型(或类型别名)会更好。我不认为今年会发生,但我想更好地了解解决方案空间。Javari,midori,pony和 rust都在方案领域内解决了一些重要问题,除此之外还有大量的研究论文。

长远来看,如果我们可以静静地消除竞争的可能,那就可以消除对大多数内存模型的需要。这可能是一个不可能的梦,但我又想更好地了解解决方案空间。

Generics 泛型

Go和非go开发者之间,没有什么会比go是否应该支持泛型(或几年前应该发生的问题)引发更激烈的争论。我不相信Go团队曾经说过“Go 不需要泛型”,我们所说的是,面临着更高优先级的问题。例如,我们对包管理的更好的支持对大多数go开发者来说比添加泛型会产生更大更直接的正面影响。但我们确实知道,对于某些子集的Go用例,缺少参数多态性是一个显著的障碍。

就我个人而言,我想能够编写通用的通道处理功能,如:

// Join makes all messages received on the input channels

// available for receiving from the returned channel.

func Join(inputs ...<-chan T) <-chan T

// Dup duplicates messages received on c to both c1 and c2.

func Dup(c <-chan T) (c1, c2 <-chan T)

我也希望通过编译时而不是在运行时捕获类型错误的方式,可以为高级数据处理抽象写入go支持,类似于FlumeJava或c#的LINQ。还可以编写任何数量的数据结构或通用算法,但是我个人认为这些更广泛的应用程序更具吸引力。

我们多年来一直在努力找到正确的方法来为go添加泛型。至少有一些过去的提案被挂起,试图设计提供一般参数多态(如chan T)已经string和[]byte的统一的东西。如果后者通过对不可变性的参数来处理,如上一节所述,那么也许简化了泛型设计的需求。

当我在2008年第一次开始考虑go的泛型时,要学习的主要例子是C#,Java,haskell和ML。在这些语言中,没有一种方法似乎完全适合Go。今天,还有更新的尝试,包括Dart,Midori,Rust和Swift。

我们大胆探索设计空间已经有几年了。这可能是再次环顾的时候了,特别是鉴于对可变性的了解以及由更新语言的附加示例集。今年我并不任务泛型会发生,但是我想能够更好地理解解决方案空间。