最近我在我设计的 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,欢迎一起来讨论。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.