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

2020-01-09 11:21:13 +08:00
 Kulics

最近我在我设计的 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

4654 次点击
所在节点    Go 编程语言
23 条回复
Kulics
2020-01-09 22:03:39 +08:00
@cyspy 你说的没错,的确不适合已经用[]去处理索引的语言。
saltsugar
2020-01-09 22:06:08 +08:00
第一眼看过去就是 lisp 啊,不过确实挺好。
对我这个对 go 语法还有点陌生的人来说,看的轻松。
saltsugar
2020-01-09 22:09:13 +08:00
看惯了 c/c++, 现在看 go 总是要转换一下思维。
现在特别赞同傻白甜设计。

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

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

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

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

© 2021 V2EX