V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
partystart
V2EX  ›  程序员

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

  •  
  •   partystart · 2021-12-24 17:43:23 +08:00 · 9349 次点击
    这是一个创建于 1070 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

    130 条回复    2021-12-29 16:36:32 +08:00
    1  2  
    hutoer
        101
    hutoer  
       2021-12-26 09:58:50 +08:00
    @cmdOptionKana 那你看看 golang make 的用法:
    make(map[string]string)
    make([]int, 2)
    make([]int, 2, 4)

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

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

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

    我自己的感受是,看代码时确实语法高亮的影响很小,但自己写代码时高亮还是有明显的辅助作用。
    cmdOptionKana
        103
    cmdOptionKana  
       2021-12-26 10:04:57 +08:00
    @hutoer make 选择这种类似重载的风格,主要因为这种自带函数有一半“占据关键词”的副作用,因此才不得不减少函数名。而在 strings, sort 之类的标准库里,就选择了显性的风格。
    cmdOptionKana
        104
    cmdOptionKana  
       2021-12-26 10:07:04 +08:00
    @hutoer 要不是怕占了关键词(保留词),换成多个函数名反而更好(从 Go 的审美角度看),我并不觉得会被骂(唯一被骂的理由只是占了保留词)。
    hutoer
        105
    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
        106
    LUO12826  
       2021-12-26 10:47:51 +08:00   ❤️ 1
    想到以前讨论谷歌 material design 和 iOS 的设计风格时,某乎上有人大篇幅论述谷歌的右划侧边栏是如何比 iOS 的底部 tab bar 优秀的。然而后来,谷歌自己又加上了底部 tab bar
    cmdOptionKana
        107
    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
        108
    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
        109
    aloxaf  
       2021-12-26 13:55:16 +08:00
    @aloxaf #108
    (此处「重载」用了引号,因为这种效果不一定非得重载才能实现
    maja
        110
    maja  
       2021-12-26 14:22:12 +08:00


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

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

    其实,我只是想说重载是有用的,而不是想证明没有重载不行。
    Buges
        112
    Buges  
       2021-12-26 14:54:18 +08:00 via Android
    @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
        113
    sxfscool  
       2021-12-26 15:35:51 +08:00
    非要和 java 一样么?为什么不去用 java
    james122333
        114
    james122333  
       2021-12-26 15:42:06 +08:00
    @Buges

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

    泛型替代不了重载

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

    constructor 完全是多余的复杂度,这个概念根本就没有必要。new FancyClassName(params) 比 FancyClassName.new(params) 没有任何优势。反而让代码难读又难写。如:
    FancyClassName FancyClassName::FancyClassName(params){
    blabla...
    }
    hutoer
        117
    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
        118
    hutoer  
       2021-12-26 17:11:54 +08:00
    instance = new FancyClassName.new(params) 这个写错了
    instance = FancyClassName.new(params)
    这个其实不算 constructor
    hutoer
        119
    hutoer  
       2021-12-26 17:18:26 +08:00
    我看错了,把 FancyClassName.new(params) 当实例函数了
    Buges
        120
    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{

    }
    Buges
        121
    Buges  
       2021-12-26 17:24:33 +08:00
    @hutoer 上面没打完,constructor 没有提供额外的好处,限制了初始化函数的命名(这也是需要重载的一个主要原因),不能为不同的情况使用不同有意义的命名。至于命名有静态检查,并且一般都有特定的 convention,没什么混乱的。另外不要只从 OOP 的角度观察,简单的函数其实更好理解。
    hutoer
        122
    hutoer  
       2021-12-26 17:31:00 +08:00
    @Buges 抛出异常和返回错误值还是不一样的,调用的地方处理也不一样。就算是返回错误值,可以 Result<Color,Error>,Error 是基类,你这样 Result<Color,ParseError>,有其他类型的错误咋返回?所以 Color::from_rgb(u8,u8,u8)和 Color::from_hex(String)返回类型没有区别。

    我那个例子,参数个数不一样的。
    dobelee
        123
    dobelee  
       2021-12-26 18:09:02 +08:00
    @hutoer 个人觉得这种写法非常糟糕。应传递颜色类型,或使用不同的方法名。
    Buges
        124
    Buges  
       2021-12-26 18:09:08 +08:00 via Android
    @hutoer return 和 throw 是不同的控制流,需要用不同的方式处理,但本质上都是返回数据给调用方,无论通过 return 还是 throw 。实际“返回”的数据类型是不一样的。
    如果签名写了 ParseError ,自然是表明该函数只可能返回这一种错误类型,如果还可以返回其他类型那自然要写成所有可能类型的 super type ,这个和 checked exception 本质是一样的。
    参数个数不一样可以用 vararg ,或者包装到一个 collection literal 里,或者写成 macro 。如果只需要几种特定数量的参数,可以分别写成 add2 、add3 等不同函数,比起导致的问题重载并不能提供足够的好处。
    agagega
        125
    agagega  
       2021-12-26 18:38:55 +08:00 via iPhone
    C++因为长期以来没有 optional 类型(而且即使有了也有额外 overhead ),所以构造函数只能用异常表示构造失败。

    而不管是 C++风格的构造函数还是手写函数构造,都存在语义不明的问题。Swift 风格的具名参数和重载可以解决,并不一定需要带不同名字的函数。

    但即使没有构造函数,这个创建对象的函数也还是需要某种方式来「创建」对象,这种 C 风格花括号的方式有是不如构造函数一样灵活。

    另外在面向对象的继承关系里,构造函数就显得很自然,非构造函数虽然也可以做到类似效果,但就会很别扭。
    fregie
        126
    fregie  
       2021-12-27 10:05:45 +08:00
    @partystart 什么叫好,什么叫不好,这几年开发者们已经用脚投票了,你觉得什么叫好,什么叫不好?
    本身都不是面向对象的语言,你非拿它来写业务,写完了还说不好用,那能说你什么?但凡明白点面向对象的也会明白 go 本身就不适合写业务,编程语言没有银弹,不同语言有各自擅长的领域。
    LoNeFong
        127
    LoNeFong  
       2021-12-27 12:20:15 +08:00
    打起来 打起来 我就喜欢看 V 站语言战争, 一堆人瞎瘠薄操心!
    赚几个钱啊?
    房贷换完没啊?
    真 tm 无聊
    ly020044
        128
    ly020044  
       2021-12-27 14:46:23 +08:00
    我就喜欢看你们打字互喷,下次你们继续哈。
    nmap
        129
    nmap  
       2021-12-28 09:57:53 +08:00
    别用不就得了嘛,人家也没逼着你用,也没让你掏一分钱
    adoal
        130
    adoal  
       2021-12-29 16:36:32 +08:00
    你是不是对 PLT 的学术界有什么误解……

    go 的错误处理硬生生把本该用 sum type 来做的事( either ok result or error info )做成了 product type ( a tuple with both ok result and error info )……哪个学术界是这么玩的?
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5553 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 45ms · UTC 08:40 · PVG 16:40 · LAX 00:40 · JFK 03:40
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.