纯吐槽帖 关于 go 的 err 和重载

2021-12-24 17:43:23 +08:00
 partystart

写了几个月的业务了 写 err 真的吐了 牵扯到序列 /反序列话、有任何文件、io 操作的地方都会有 error

报的那么多 error 有啥用? 报那么多 error 没能解决问题 第一行成功 下面的几处 error 第一行的 error 岂不是白打了? 这种与业务无强关联的地方 与业务嵌套这么深 直接全局异常捕捉不就行了?

还有都 21 世纪了 居然不支持重载 输出一样 输入参数不一样 不能重载 就很无语。我写个方法功能一样 还得另外起个名字

这群设计者是学术界呆太久了?

10254 次点击
所在节点    程序员
130 条回复
hutoer
2021-12-26 09:58:50 +08:00
@cmdOptionKana 那你看看 golang make 的用法:
make(map[string]string)
make([]int, 2)
make([]int, 2, 4)

按照你说的风格,要是变成多个函数了,会不会被吐槽?
cmdOptionKana
2021-12-26 10:01:13 +08:00
@hutoer 当然,我第一次听这种说法的时候,也是愕然了一下,但人家说的是有点道理。

说个题外话,另外一个 Go 语言相关的更震惊我说法是:语法高亮没有必要。

Rob Pike 平时使用一个自己做的编辑器“Acme”是不带语法高亮的,Go 团队里的另一个重要人物 Russ Cox 也使用 Acme 。事实上,可能有些人甚至没有察觉,Go 的文档服务 godoc (现在已迁移到 go.dev )里的示例代码全都没有语法高亮,但只要脑子里想的是如何解决问题,就会发现没有语法高亮一点影响都没有,甚至都忘记了自己看的代码是没有高亮的。

我自己的感受是,看代码时确实语法高亮的影响很小,但自己写代码时高亮还是有明显的辅助作用。
cmdOptionKana
2021-12-26 10:04:57 +08:00
@hutoer make 选择这种类似重载的风格,主要因为这种自带函数有一半“占据关键词”的副作用,因此才不得不减少函数名。而在 strings, sort 之类的标准库里,就选择了显性的风格。
cmdOptionKana
2021-12-26 10:07:04 +08:00
@hutoer 要不是怕占了关键词(保留词),换成多个函数名反而更好(从 Go 的审美角度看),我并不觉得会被骂(唯一被骂的理由只是占了保留词)。
hutoer
2021-12-26 10:38:50 +08:00
@cmdOptionKana 我倒是觉得重载能降低心智负担、提升代码简洁渡,例如:
data := make([]int, 2)
data := make([]int, 2, 4)

比下面的简洁
data := makeXX1([]int, 2)
data := makeXX2([]int, 2, 4)

还有这种( Color 是构造函数)
color = new Color(0,0,255)

color = new Color("#FFFFFF")

color = new Color(Color::RED)

比下面的简洁
color = new Color()
color.setByRGB(0,0,255)

color = new Color()
color.setByHEX("#FFFFFF")

color = new Color()
color.setByName(Color::RED)
LUO12826
2021-12-26 10:47:51 +08:00
想到以前讨论谷歌 material design 和 iOS 的设计风格时,某乎上有人大篇幅论述谷歌的右划侧边栏是如何比 iOS 的底部 tab bar 优秀的。然而后来,谷歌自己又加上了底部 tab bar
cmdOptionKana
2021-12-26 11:05:22 +08:00
这个不需要重载啊,变长参数就可以了。
data := make([]int, 2)
data := make([]int, 2, 4)


而这个
color = new Color(0,0,255)
color = new Color("#FFFFFF")

Go 的习惯是
c = color.New(0,0,255)
c = color.ByHex("#FFFFFF")

从简洁的角度看差别不大。
(其中 color 是 package 名字,New 和 ByHex 是 public 函数)
aloxaf
2021-12-26 13:48:37 +08:00
@hutoer #99

这个例子举得不好,换我我肯定不会这么设计——「 Color::Color("#FFFFFF")」这种,显然是可能初始化失败的。如果并作一个 API ,在强迫你显式处理错误的语言中,就不得不为明明不可能出错的逻辑写上错误处理代码。

举个我觉得比较常见的「重载」更好的例子:
string.replace('a', 'b')
string.replace("abc", 'a')
string.replace("abc", "def")
string.replace("abc", a_function_pointer)
...(以下省略若干种排列组合)...
aloxaf
2021-12-26 13:55:16 +08:00
@aloxaf #108
(此处「重载」用了引号,因为这种效果不一定非得重载才能实现
maja
2021-12-26 14:22:12 +08:00


call/cc 用过没? 这玩意比宁那全局异常捕捉靠谱多了。
hutoer
2021-12-26 14:33:49 +08:00
@cmdOptionKana golang make 会根据第一个参数做不同的处理,行为更像重载,变长参数不行。
另外,变长参数我反倒觉得是不良设计,尽量少用。

你说的 color.ByHex("#FFFFFF")这种方式,已经是牺牲可读性换取简洁性了。相对我的例子,还不是 OOP 的方式。

其实,我只是想说重载是有用的,而不是想证明没有重载不行。
Buges
2021-12-26 14:54:18 +08:00
@hutoer go 的 make 只是缺乏泛型的 trick ,没啥好说的。
另外不要被 OOP 荼毒太深,constructor 本身就是糟粕,可读性简洁性都不如直接用函数。
Color::from_rgb(u8,u8,u8)和 Color::from_hex(String)返回类型都不一样,后者是可失败的(虽然在异常的语言中被隐藏了),难道该用同一个名字吗?
另外就算需要所谓的“重载”效果,用泛型是更好的选择。Color::from<T: Into<Color>>(t:T)->Color 甚至比你写多个重载的函数更“OOP”。
sxfscool
2021-12-26 15:35:51 +08:00
非要和 java 一样么?为什么不去用 java
james122333
2021-12-26 15:42:06 +08:00
@Buges

说到重点了 又不一定得写 oop
hutoer
2021-12-26 16:01:17 +08:00
@Buges Color::from_rgb(u8,u8,u8)和 Color::from_hex(String)返回类型咋不一样了?都是 Color 实例呀。

泛型替代不了重载

至于“constructor 本身就是糟粕,可读性简洁性都不如直接用函数”,这个我就不讨论了,大家看问题的面不一样。
Buges
2021-12-26 16:31:49 +08:00
@hutoer 因为后者可能会失败。粗略地说,前者返回 Color ,后者返回 Color | ParseError 。异常的语言可能会隐藏这一点,但用了 checked exception 同样可以体现出来。
泛型就是达到“重载”的效果最合适的方式,泛型代替不了重载的地方就不该用重载。

constructor 完全是多余的复杂度,这个概念根本就没有必要。new FancyClassName(params) 比 FancyClassName.new(params) 没有任何优势。反而让代码难读又难写。如:
FancyClassName FancyClassName::FancyClassName(params){
blabla...
}
hutoer
2021-12-26 17:08:45 +08:00
@Buges “后者返回 Color | ParseError”,这个说法不恰当,返回的还是 Color ,Parse 出错只不过抛异常。
前者函数内出错误( Color 这例子简单,大面上不会出错),也是会抛异常的。

“泛型代替不了重载的地方就不该用重载”,这个说绝对,下面这种:
add(float a,float b)
add(int a,int b,int c)
add(char a,char b,char c)

业务毕竟多种多样的,有重载还是利于简化代码的。

方式 1
FancyClassName FancyClassName::FancyClassName(params){
blabla...
}

instance = new FancyClassName(params)

方式 2
FancyClassName FancyClassName::new(params){
blabla...
}

instance = new FancyClassName()
instance.new(params)

我觉得还是方式 1 更好、更简洁。而且方式 1 规范了 constructor
方式 2 可以是 new 、create 、init 、build....,反而很乱

如果你说可以这样:
instance = new FancyClassName.new(params)

那么 new 就是 constructor ,换了名字而已
hutoer
2021-12-26 17:11:54 +08:00
instance = new FancyClassName.new(params) 这个写错了
instance = FancyClassName.new(params)
这个其实不算 constructor
hutoer
2021-12-26 17:18:26 +08:00
我看错了,把 FancyClassName.new(params) 当实例函数了
Buges
2021-12-26 17:19:36 +08:00
@hutoer 异常只不过是一种不同的控制流,返回 Color throws ParseError 和返回 Result<Color,ParseError>本质上是一样的,都是返回数据,而这两者返回的数据“类型不同”。

你这个例子是完全可以用泛型的:
fn add<A: IAddable<Output=A>>(a:A,b:A,c:A)->A{

}

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/824287

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX