V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
Kulics
V2EX  ›  Go 编程语言

一种兼容性更好的 Go 泛型设计

  •  
  •   Kulics ·
    kulics · 2020-01-09 11:21:13 +08:00 · 4463 次点击
    这是一个创建于 1540 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近我在我设计的 Lite 语言中探索了一种新的泛型语法,因为 Lite 从 Go 中借鉴了不少语法,所以这种泛型语法对于 Go 可能也有一定的参考价值。

    identifier<T> 这种最主流的语法比较大的问题是与比较操作符冲突,同时也与位操作符冲突,因此我不赞同这种设计。

    Scala 的identifier[T] 语法比前面的语法有更好的观感,但解决了上面的冲突之后又与索引语法 identifier[index] 产生了新的冲突, 为此 Scala 的索引语法改为了 identifier(index)。 这对于已经采用[]作为索引的语言来说效果也不算很好。

    在 Go 的草案中,声明泛型使用 (type T) 的方式,这不会造成冲突,因为type是关键字,但是调用的时候依然需要编译器进行更多判断才能解决 identifier(type)(params) 的冲突问题,虽然它比以上的方案都好,但仍然不能让我满意。

    偶然的机会让我想起了 OC 中方法调用的特殊语法,这给了我一种新语法的灵感。

    如果我们将标识符和泛型组成一个整体,一起放入 [] 如何?

    我们可以得到这个语法 [identifier T], 这个语法不会与索引语法产生冲突,因为它必然存在至少两个元素,中间使用空格隔开。

    当存在多个泛型时,我们可以这样写 [identifier T, V],同样不会与现有的语法产生冲突。

    将这个语法代入 Go 中,我们可以得到下面的例子。

    E.g.

    type [Item T] struct {
        Value T
    }
    
    func (item [Item T]) Print() {
        println(item.Value)
    }
    
    func [TestGenerics T, V]() {
        a := [Item T]{}
        a.Print()
        b := [Item V]{}
        b.Print()
    }
    
    func main() {
        [TestGenerics int, string]()
    }
    

    这看起来非常清晰。

    使用 [] 的另一个好处是,与 Go 原本的 Slice 和 Map 语法有一定传承性,不会造成割裂感。

    []int -> [slice int]
    
    map[string]int -> [map string, int]
    

    我们可以造一个更复杂的例子

    var a map[int][]map[string]map[string][]string
    
    var b [map int, [slice [map string, [map string, [slice string]]]]]
    

    这个例子依然能保持比较清晰的效果,同时对编译造成的影响很小。

    我已经在 Lite 中实现并测试了这个语法,在 Lite 中效果很好,没有产生歧义。

    这个设计也许值得引起讨论,我已经在 github 上提交了一个 issue,欢迎一起来讨论。

    https://github.com/golang/go/issues/36457

    23 条回复    2020-01-09 22:09:13 +08:00
    codehz
        1
    codehz  
       2020-01-09 11:35:47 +08:00
    L i s p
    直接说 S-Exp 就好了(
    codehz
        2
    codehz  
       2020-01-09 11:36:49 +08:00
    甚至逗号都不需要,直接用空格区分多参数(
    不过多参数是万恶之源,应该柯里化一下,做成 * -> * -> * 的模式(
    Kulics
        3
    Kulics  
    OP
       2020-01-09 11:40:43 +08:00
    @codehz hhhhhhh 站在巨人的肩膀上
    dodo2012
        4
    dodo2012  
       2020-01-09 11:43:29 +08:00
    var b [map int, [slice [map string, [map string, [slice string]]]]]

    这个套的眼花了都,不觉得有多清晰,使用<T>的方式,更多是大家都是这样用的,更熟悉,
    IsaacYoung
        5
    IsaacYoung  
       2020-01-09 11:46:20 +08:00
    <>
    Kulics
        6
    Kulics  
    OP
       2020-01-09 12:58:43 +08:00
    @dodo2012
    var b map<int, slice<map<string, map<string, slice<string>>>>>
    var b [map int, [slice [map string, [map string, [slice string]]]]]

    相比之下,我还是觉得比<T>的方式清晰一些。
    liuguang
        7
    liuguang  
       2020-01-09 14:04:08 +08:00
    换汤不换药
    thisisgpy
        8
    thisisgpy  
       2020-01-09 14:12:57 +08:00
    楼主你的 Lite 语言,运算符设计的太复杂了
    kifile
        9
    kifile  
       2020-01-09 14:14:06 +08:00
    觉得只是 [] 比 <> 占的显示控件笑了,[doge]
    kifile
        10
    kifile  
       2020-01-09 14:14:24 +08:00
    显示空间小了
    hantsy
        11
    hantsy  
       2020-01-09 14:14:47 +08:00
    用 Java 多了,还是感觉<>好
    inhzus
        12
    inhzus  
       2020-01-09 14:14:58 +08:00 via Android
    可能是我 go 写太多了吗?我觉得楼主最后举的例子 go 的例子看起来挺显然的…
    imnaive
        13
    imnaive  
       2020-01-09 14:50:29 +08:00
    这个不是设计,是一种语法吧
    fcten
        14
    fcten  
       2020-01-09 15:02:34 +08:00
    "identifier<T> 这种最主流的语法比较大的问题是与比较操作符冲突,同时也与位操作符冲突,因此我不赞同这种设计。"

    这个前提就不对。操作符和类型声明出现的位置完全不一样,在解析成 AST 之前就可以区分清楚了,对于编译器来说是属于比较容易解析的语法。
    就比如括号 ( ) ,非常常见的符号,并且有多种语法,也没有冲突的问题。

    语法首先是为语义服务的。先设计功能,再设计语法。所以,到底冲不冲突,最终是要看该语法被用来支持哪些语义。用其它语言的泛型设计来推断 Go 的语法本身也是有问题的。
    cyspy
        15
    cyspy  
       2020-01-09 18:35:58 +08:00
    scala 没有索引语法,只是 apply 函数而已
    Kulics
        16
    Kulics  
    OP
       2020-01-09 21:56:05 +08:00
    @thisisgpy 的确是复杂,一开始特性少还好,后来特性越来越完备,复杂性就升上去了。只要功能多,复杂是必然的了。
    Kulics
        17
    Kulics  
    OP
       2020-01-09 21:58:20 +08:00
    @kifile 主要还是类似 s-expr 的语法比<T>形式的界限看起来清晰一些,把[]换成<>其实也会比原来的看起来清晰。
    Kulics
        18
    Kulics  
    OP
       2020-01-09 21:59:21 +08:00
    @hantsy 可是不适合 go 啊,go 草案已经排斥了<>。不然也不会有机会来讨论这个语法。
    Kulics
        19
    Kulics  
    OP
       2020-01-09 21:59:41 +08:00
    @imnaive 这么说也对,语法设计。
    Kulics
        20
    Kulics  
    OP
       2020-01-09 22:00:49 +08:00
    @fcten 曾经我也是这么想,直到后来自己写了个编译器才发现问题。
    Kulics
        21
    Kulics  
    OP
       2020-01-09 22:03:39 +08:00
    @cyspy 你说的没错,的确不适合已经用[]去处理索引的语言。
    saltsugar
        22
    saltsugar  
       2020-01-09 22:06:08 +08:00
    第一眼看过去就是 lisp 啊,不过确实挺好。
    对我这个对 go 语法还有点陌生的人来说,看的轻松。
    saltsugar
        23
    saltsugar  
       2020-01-09 22:09:13 +08:00
    看惯了 c/c++, 现在看 go 总是要转换一下思维。
    现在特别赞同傻白甜设计。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5504 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 08:19 · PVG 16:19 · LAX 01:19 · JFK 04:19
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.