最近我在我设计的 Lite 语言中探索了一种新的泛型语法,因为 Lite 从 Go 中借鉴了不少语法,所以这种泛型语法对于 Go 可能也有一定的参考价值。
identifier<T>
这种最主流的语法比较大的问题是与比较操作符冲突,同时也与位操作符冲突,因此我不赞同这种设计。
Scala的identifier[T]
语法比前面的语法有更好的观感,但解决了上面的冲突之后又与索引语法identifier[index]
产生了新的冲突, 为此Scala的索引语法改为了identifier(index)
。这对于已经采用[]
作为索引的语言来说效果也不算很好。
在 Go 的草案中,声明泛型使用(typeT)
的方式,这不会造成冲突,因为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,欢迎一起来讨论。
![]() | 1 codehz 2020-01-09 11:35:47 +08:00 L i s p 直接说 S-Exp 就好了( |
![]() | 2 codehz 2020-01-09 11:36:49 +08:00 甚至逗号都不需要,直接用空格区分多参数( 不过多参数是万恶之源,应该柯里化一下,做成 * -> * -> * 的模式( |
4 dodo2012 2020-01-09 11:43:29 +08:00 var b [map int, [slice [map string, [map string, [slice string]]]]] 这个套的眼花了都,不觉得有多清晰,使用<T>的方式,更多是大家都是这样用的,更熟悉, |
![]() | 5 saacYoung 2020-01-09 11:46:20 +08:00 <> |
![]() | 6 Kulics OP @dodo2012 var b map<int, slice<map<string, map<string, slice<string>>>>> var b [map int, [slice [map string, [map string, [slice string]]]]] 相比之下,我还是觉得比<T>的方式清晰一些。 |
7 liuguang 2020-01-09 14:04:08 +08:00 换汤不换药 |
8 thisisgpy 2020-01-09 14:12:57 +08:00 楼主你的 Lite 语言,运算符设计的太复杂了 |
9 kifile 2020-01-09 14:14:06 +08:00 觉得只是 [] 比 <> 占的显示控件笑了,[doge] |
10 kifile 2020-01-09 14:14:24 +08:00 显示空间小了 |
11 hantsy 2020-01-09 14:14:47 +08:00 用 Java 多了,还是感觉<>好 |
![]() | 12 inhzus 2020-01-09 14:14:58 +08:00 via Android 可能是我 go 写太多了吗?我觉得楼主最后举的例子 go 的例子看起来挺显然的… |
![]() | 13 imnaive 2020-01-09 14:50:29 +08:00 这个不是设计,是一种语法吧 |
![]() | 14 fcten 2020-01-09 15:02:34 +08:00 "identifier<T> 这种最主流的语法比较大的问题是与比较操作符冲突,同时也与位操作符冲突,因此我不赞同这种设计。" 这个前提就不对。操作符和类型声明出现的位置完全不一样,在解析成 AST 之前就可以区分清楚了,对于编译器来说是属于比较容易解析的语法。 就比如括号 ( ) ,非常常见的符号,并且有多种语法,也没有冲突的问题。 语法首先是为语义服务的。先设计功能,再设计语法。所以,到底冲不冲突,最终是要看该语法被用来支持哪些语义。用其它语言的泛型设计来推断 Go 的语法本身也是有问题的。 |
15 cyspy 2020-01-09 18:35:58 +08:00 scala 没有索引语法,只是 apply 函数而已 |
![]() | 17 Kulics OP @kifile 主要还是类似 s-expr 的语法比<T>形式的界限看起来清晰一些,把[]换成<>其实也会比原来的看起来清晰。 |
![]() | 22 saltsugar 2020-01-09 22:06:08 +08:00 第一眼看过去就是 lisp 啊,不过确实挺好。 对我这个对 go 语法还有点陌生的人来说,看的轻松。 |
![]() | 23 saltsugar 2020-01-09 22:09:13 +08:00 看惯了 c/c++, 现在看 go 总是要转换一下思维。 现在特别赞同傻白甜设计。 |