设为首页 - 加入收藏 ASP站长网(Aspzz.Cn)- 科技、建站、经验、云计算、5G、大数据,站长网!
热搜: 数据 创业者 手机
当前位置: 首页 > 综合聚焦 > 编程要点 > 语言 > 正文

Go 语言设计失误,缺乏远见?

发布时间:2021-11-04 09:51 所属栏目:51 来源:互联网
导读:大家好,我是煎鱼。 前段时间我有一个朋友在某站点上摸鱼时,给我甩来一个主题为《golang 设计者是如何偿还技术债的》链接。 说是让我学习、围观一下社区观点,早日好修成正果,本鱼表示满脸问号。 原回答如下图: 主要是以极短的话语表述 Go 语言的 泛型、
大家好,我是煎鱼。
 
前段时间我有一个朋友在某站点上摸鱼时,给我甩来一个主题为《golang 设计者是如何偿还技术债的》链接。
 
说是让我学习、围观一下社区观点,早日好修成正果,本鱼表示满脸问号。
 
原回答如下图:
 
 
 
主要是以极短的话语表述 Go 语言的 “泛型、异常、channel、annotation、模块依赖” 的设计是失误的。
 
说是没有向各种编程语言的 “最佳实践” 各取所需。
 
那些故事
刚好煎鱼也入门 Go 没几天,偶尔翻过 issues 和 proposal,看了一点点历史事件。
 
 
 
图来自 Introduction to Golang
 
也从我的观点来围观一下 Go 官方这些年为特性挣扎过的那些事。
 
涉及:
 
泛型。
错误处理。
依赖管理。
注解。
泛型
为什么 Go 语言这么久都没有泛型,是不是 Go 官方不够 “聪明”,抄作业都不会抄。这显然是不对的。
 
有如下几点原因:
 
泛型本质上并不是绝对的必需品。
泛型不是 Go 语言的早期目标。
其他 feature 更重要,把精力放在这些上面,Go 团队人力很有限的。
历史尝试
在以往的尝试中,Go 团队有人进行过不少的泛型 proposal 试验。基本时间线(via @changkun)如下:
 
简述 时间 作者
Type Functions 2010年 Ian Lance Taylor
Generalized Types 2011年 Ian Lance Taylor
Generalized Types v2 2013年 Ian Lance Taylor
Type Parameters 2013年 Ian Lance Taylor
go:generate 2014年 Rob Pike
First Class Types 2015年 Bryan C.Mills
Contracts 2018年 Ian Lance Taylor, Robert Griesemer
Contracts 2019年 Ian Lance Taylor, Robert Griesemer
Redundancy in Contracts(2019)'s Design 2019年 Ian Lance Taylor, Robert Griesemer
Constrained Type Parameters(2020, v1) 2020年 Ian Lance Taylor, Robert Griesemer
Constrained Type Parameters(2020, v2) 2020年 Ian Lance Taylor, Robert Griesemer
Constrained Type Parameters(2020, v3) 2020年 Ian Lance Taylor, Robert Griesemer
我们观察一下,10 年过去了,Ian Lance Taylor 依然在开展泛型提案,持续地在思考着 Go 泛型。
 
坚持思考,这一点值得我们学习。
 
下一步计划
在 2021 年尾巴的我们,明年(2022年) Go1.18 左右就可以见到 Go 泛型,基本跑不了。
 
在出来前可以看看《Go 1.17 支持泛型了?具体怎么用》,可以作为玩具用了。
 
接下来可以预见泛型出来后,一堆工具库和数据结构很大可能会被逐步改写,像是《Go 提案:增加泛型版 slices 和 maps 新包》,早已摩拳擦掌。
 
届时 Go 源码类别的书的部分内容也会失时效,需要关注 Go 版本的时效性。
 
错误处理
在日常工程中,我们写的、看到最多的可能就是这一段标志性 Go 代码:
 
func main() { 
 x, err := foo() 
 if err != nil { 
   // handle error 
 } 
 y, err := foo() 
 if err != nil { 
   // handle error 
 } 
 z, err := foo() 
 if err != nil { 
   // handle error 
 } 
 s, err := foo() 
 if err != nil { 
   // handle error 
 } 
这是在业内被吐槽的最多的,甚至都可以用来作为 Gopher 的互认。
 
设计方向
那 Go 是瞎设计的吗,就粗制滥造,搞个错误 err 的返回约定惯例。像是:
 
func foo() err { 
    return nil 
其实并不是,Go 团队在设计上有意识地选择了显式的设计方向,如下:
 
使用显式错误结果。
使用显式错误检查。
这和其他语言不一样 ,是由于 Go 团队也认识到了异常处理的不可见错误检查所带来的问题。
 
设计草案有一部分是受到了这些问题的启发。如下:
 
 
 
目前 Go 官方也没有打算去掉 “显式” 这一做法,新版 Go2 错误处理的核心目标是:“错误检查更加轻便,减少专门用于错误检查的 Go 程序代码的数量和所花费的时间。”。
 
从 Go2 的趋势来看,主要是增加关键字和修饰来解决这个问题,相当于是堆积木了,而不是直接把他干掉的。
 
这在 Go 核心团队内是非常明确的。
 
依赖管理
Go 语言在一开始是完全基于 GOPATH 作为依赖管理的模式,当时也闹了不少的争议出来。有以下核心问题:
 
依赖要手动拉取和下载,没有强版本化的概念,开发者很难受(例如:不兼容升级、要拉取同一份)。
 
依赖和工程代码必须在 GOPATH 下才能运行,不能任意摆放。
 
所以在 Go1.0~Go1.11 中,各路神仙发招,社区出现了各种诸如 dep、glide、godep 等依赖包管理工具。
 
时间线
后续 Go 团队在 Russ Cox 的强势推进下,力排众议,推动 Go modules 的发展:
 
 
 
时间线如下:
 
Go1.11 起开始推进 Go modules(前身 vgo)。
Go1.13 起不再推荐使用 GOPATH 的使用模式。
Go1.14 表示已经准备好,可以用在生产上(ready for production)了。
为什么这么晚
为什么 Go modules 这么晚才诞生,这是不是就是 Go 团队的设计失误呢?
 
我认为,是也不是。
 
Go 的诞生一开始是为了解决 Google 几位大佬自己的痛点。
 
在 Google 的依赖管理上,本身是大仓库(Monorepo)的模式,企业内部有自己一整套工具和流程,设计之初没有这块的强诉求。
 
如下:
 
 
 
图来自 Mono Repo vs Multi Repo
 
有兴趣的读者详细可阅读《Why Google Stores Billions of Lines of Code in a Single Repository》,
 
Go 在社区开源后,大规模使用后这个问题就爆发了,社区自行释出了方案。可惜,五花八门,也都没有解决好。官方队伍就自己上手了。
 
要知道,没有技术方案是完美的。Go modules 也被不少人所吐槽,存在争议。
 
注解
Go 开发者中有大部分同学都有其他语言的使用经验。在其他语言中,注解是一个强大的工具,没得用会很不习惯。
 
 
 
图片来自网络
 
甚至有听过没有注解,就自嘲不会 “写” 代码了,所以一上来就找 Go 语言的注解怎么用了。
 
一些疑惑
我有一个朋友,经常会听到如下疑惑,甚至无奈的发问:
 
“怎么样在函数前声明,直接开启事务?”
 
"为什么 Java 可以完美注解,Go 就不行,难以理解,我无法接受..."
 
“那 Go 支持什么程度的注解?”
 
Go 的 “注解” 支撑的非常有限,基本都是 //go build、go:generate 这类辅助,达不到标准的装饰器的作用。
 
为什么不支持
没有全面的支持注解来做装饰器,显然不算 Go 的设计失误,这是刻意为之,这是与错误处理的设计理念相关联。
 
Go issues 上有人提过类似的提案:
 
 
 
Go Contributor @ianlancetaylor 给出了明确的答复,Go在设计上更倾向于明确的、显式的编程风格。
 
优缺点如下:
 
优势:不知道 Go 能从添加装饰器中得到什么好处,没能在 issues 上明确论证。
缺点:是明确的,会存在意外设置的情况。
因如下原因,没有接受注解:
 
对比现有代码方法,这种装饰器的新的方法没有提供比现有方法更多的优势,大到足矣推翻原有的设计思路。
社区内的投票,支持的也很少(基于表情符号的投票),用户反馈不多。
可能有小伙伴会说了,有注解做装饰器了,代码会简洁不少。
 
但其实 Go 团队的态度很明确:
 
 
 
Go 认为可读性更重要,如果只是额外多写一点代码,在权衡后,还是可以接受的。

(编辑:ASP站长网)

    网友评论
    推荐文章
      热点阅读