Go Wiki: Go 2 Generics 反馈
此页面旨在收集和整理关于 Go 2 Contracts (Generics) 草案设计 的反馈。
可以在 https://go-lang.org.cn/cl/149638 找到该语法的原型实现,该实现可能已针对 Go 仓库的最新版本进行修补。
请在您的博客、Medium、GitHub Gists、邮件列表、Google Docs 等地方发布您的反馈。然后请在此处链接。
随着反馈的增多,请随时按反馈类型对本页面进行组织或重新组织。
支持
-
Roger Peppe,“Go Contracts 的使用场景:泛型的 mgo”,2018 年 9 月
-
Richard Fliam,“Go2 Generics 允许你构建自然数”,2018 年 8 月
补充(有修改的支持意见)
-
Matt McCullough,“走向清晰:Contracts 的 Go 语法更改”和“Go Contracts 的尖括号分隔符”,2020 年 5 月
-
Gert Cuykens,“泛型语法与常规 Go 代码完全分离”,2020 年 1 月
-
Court Fowler,“一个懒惰程序员对更新设计的思考”,2019 年 9 月
-
Andrew Phillips,“示例类型作为 Contracts”,2019 年 8 月
-
Alexey Nezhdanov,“语法简化提案”,2019 年 8 月
-
Bryan Ford,“Go 2 Generics 中的类型参数是否足够泛型?”,2019 年 7 月
-
Tom Levy,“Go 2 Generics 反馈”,2019 年 6 月
-
Ole Bulbuk,“为什么 Go Contracts 对不断变化的 Go 社区来说是个坏主意”,2019 年 4 月
-
Tony Mottaz,“Go 泛型类型和导入注入”,2019 年 3 月
-
Gustavo Bittencourt,“Contracts 仅用于泛型类型”,2019 年 3 月
-
David Heuschmann,“使用括号作为类型参数列表的问题”,2019 年 2 月
-
Gustavo Bittencourt,“带方法的 Contract”,2019 年 2 月
-
Chris Siebenmann,“Go 2 Generics:Contracts 太聪明了”,2018 年 11 月
-
Chris Siebenmann,“Go 2 Generics:让 Contracts 对人们更具可读性的一种方法”,2018 年 11 月
-
Chris Siebenmann,“Go 2 Generics:接口不是类型约束的正确模型”,2018 年 11 月
-
alanfo,“结合收到的反馈对 Go 泛型草案设计提出的修改建议”,2018 年 10 月
-
Andy Balholm,“枚举式和结构式 Contracts”,2018 年 10 月
-
Burak Serdar,“类型即 Contracts”,2018 年 10 月
-
Patrick Smith,“Go 泛型用于内置和用户定义的类型参数”,2018 年 9 月
-
Jacob Carlborg,“Go 2 D 草案修正”,2018 年 9 月
-
alanfo,“简化的泛型约束系统”,2018 年 9 月
-
Paul Borman,“简化语法”,2018 年 9 月
-
mrwhythat,“Go 2 Generics 草案笔记”,2018 年 9 月
-
Roger Peppe,“运算符重载”,2018 年 9 月
-
Peter McKenzie,“替代泛型语法”,2018 年 9 月
-
Ted Singer,“语法的设计目标是帮助人类阅读”,2018 年 9 月
-
alanfo,“对 Go 2 Generics 草案设计的建议修正”,2018 年 9 月
-
Dean Bassett,“如果我们使用 Contracts,请允许对字符串进行一元 + 操作”,2018 年 9 月
-
Kevin Gillette,“关于 Go 2 Generics 草案”,2018 年 9 月
-
jimmy frasche,“不允许嵌入类型参数”,2018 年 8 月
-
Javier Zunzunegui,“编译 Generics”,2018 年 8 月
-
Liam Breck,“请不要篡改函数签名”,2018 年 8 月
-
DeedleFake,“对 Go 2 设计草案的反馈”,2018 年 8 月
-
Roberto (empijei) Clapis,“难以阅读的语法”,2018 年 8 月
-
Dominik Honnef,“我对 Go Generics 草案的看法”,2018 年 8 月
反向提案
-
dotaheor,“将泛型声明为带有泛型参数的迷你包”,2020 年 8 月
-
Beoran,“卫生宏”,2019 年 6 月
-
Randy O’Reilly,“泛型原生类型”,2019 年 6 月
-
Michal Štrba,“放弃限制类型”,2019 年 5 月
-
Eric Miller,“使用 const 结构字段的简单泛型”,2019 年 3 月
-
dotaheor,“统一 Go 内置和自定义泛型的解决方案”,2019 年 2 月
-
Quentin Quaadgras,“无语法更改,1 个新类型,1 个新内置”,2018 年 12 月
-
Andy Balholm,“Contracts 和 Adaptors”,2018 年 11 月
-
Dean Bassett,“Contract 嵌入”,2018 年 10 月
-
Patrick Smith,“带 Adaptors 的 Go Generics”,2018 年 10 月
-
Ian Denhardt,“Go Generics:关于使用接口代替 Contracts 的具体提案。”,2018 年 10 月
-
Arendtio,“受接口启发的 Go 泛型”,2018 年 9 月
-
Scott Cotton,“关于统一 Contracts 和接口的草案提案修改” (diff),2018 年 9 月
-
ohir,“CGG,Craftsman Go Generics”,2018 年 9 月
-
~~Dean Bassett,“使用接口代替 Contracts”,2018 年 9 月~~
我已提交第二个提案(“contract embedding”),列在下面,解决了这个提案中的问题。 -
dotaheor,“将 contract 和代码结合起来,并将泛型视为具有多个输出的编译时调用”,2018 年 9 月。(不时更新)
-
Aleksei Pavliukov,“扩展 type 和 func 关键字”,2018 年 9 月
-
Han Tuo,“泛型作为一种类型——type T generic {int, float64}”,2018 年 9 月
-
Nate Finch,“Go2 Contracts 走得太远”,2018 年 9 月
-
Roger Peppe,“Go Contracts 作为类型结构”,2018 年 9 月
-
Axel Wagner,“放弃 Contracts”,2018 年 9 月
-
Matt Sherman,“泛型作为内置类型类”,2018 年 9 月
-
Roger Peppe,“修订后的泛型提案”,2018 年 9 月
-
Steven Blenkinsop,“对 Go2 Contracts 草案设计的回复——辅助类型”,2018 年 9 月
-
Dave Cheney,“也许为 Go 添加泛型最终还是关于语法的问题”,2018 年 9 月
-
Christian Surlykke,“Go Generics 的约束”,2018 年 9 月
-
go-nuts 上的部分 Gophers,“统一接口和 Contracts”,2018 年 8 月
-
Roger Peppe,“Go 泛型反馈”,2018 年 8 月
-
Ruan Kunliang,“包级别泛型”,2018 年 8 月
-
Emily Maier,“具体讨论泛型”,2018 年 8 月
反对
- Tokyo Gophers,“Go 2 草案设计反馈活动评论”,2018 年 10 月
-
Jason Moiron,“关于 Go2 Generics 草案的笔记”,2018 年 9 月
-
Yoshiki Shibukawa,“对泛型/Contracts 提案的反馈”,2018 年 9 月
添加您的反馈
请按以下格式编写所有条目。
- 您的姓名,“标题”,月 年
为了更容易查看新反馈,请创建一个 Gist。另外,请将新条目放在类别列表的顶部,以帮助保持列表按时间倒序排序。
快速评论
-
Chester Gould:此提案唯一的问题在于,显式 Contracts 似乎只会增加代码的冗长性,这违背了代码简洁易读的目标。与其编写显式 Contracts,不如使用我们实际编写的代码作为一种“隐式 Contract”,这样会更简洁优雅。这里提供了一个示例:这里。我承认这在此处得到了解决,但我不同意显式 Contracts 是解决问题的办法。在我看来,Contracts 与接口提供的功能非常相似,因此应该扩展接口的功能,使其行为更接近 Contracts,而不是向语言添加一种全新的语句类型。
-
Izaak Weiss:大部分讨论都集中在如何实现 Contracts 或类似功能。然而,大多数“有用示例”并不需要 Contracts;它们只需要参数化多态。在没有 Contracts 的情况下,编写类型安全的
Merge
或SortSlice
是可能的。对于更简单的 Contracts,我们可以通过高阶函数来实现。泛型哈希表可以参数化一个具有Hash
方法的类型,或者在构造时接受一个func(K) int64
来对其键进行哈希。如果需要更多函数,可以声明包含这些函数的结构体作为伪 Contracts,然后将它们传递给泛型函数。这使得 Go 的多态性变得简单、显式,并为未来关于 Contracts 或其他机制的进一步创新留下了空间,同时允许现在实现泛型类型的大部分好处。 -
Christoph Hack:我刚刚观看了 Alexandrescu 的最新演讲The next big Thing。他声称“Concepts 是在浪费时间”,并提出了一种完全不同、功能更强大的方向(甚至与 C++ 今天所能实现的一切相比)。Go 已经拥有了大多数所需的功能,例如反射和检查类型是否实现了可选接口。唯一缺失的是代码生成。例如,
json.Marshal
通过反射工作得很好,但如果它还能(可选地)通过实现一个由编译器自动调用的 Go 函数来生成代码,那么我们就拥有了一切。这可能一开始听起来很疯狂,并且示例可能看起来很冗长,但我认为 Alexandrescu 说得有道理。想想 gqlgen 与其他基于反射的 graphql-libs 的对比就知道了。请观看他的演讲! -
Bodie Solomon:我发现泛型设计有点令人困惑和不透明。请考虑整合一些Zig 的漂亮 comptime 函数中的概念!Go 2 泛型的设计很巧妙,但我认为它违背了 Go 传统上将简单运行时语义与简单语法紧密耦合的原则。此外,Go 的一个主要问题,阻碍了它在我希望使用的所有地方成为一个可行的竞争者,是我无法摆脱 GC 和运行时。我曾强烈希望 Go 2 会引入仅限编译时的泛型,这样我就可以可靠地避免在不想使用的地方使用动态接口,而无需依赖代码生成。不幸的是,这似乎将由编译器决定,而无需我的输入。请至少考虑允许用户将泛型约束为仅限编译时解析,也许作为 Contract 的一种属性,拒绝编译动态类型以满足 Contract。
-
Dag Sverre Seljebotn:C++ 在人们滥用元编程(“泛型”)进行编译时元编程方面存在巨大问题。我真希望 Go 能像 Julia 一样,提供卫生宏。即使严格限制在编译时边界内,并且不进行运行时代码生成,这至少也能避免 C++ 世界中因其模板系统而产生的各种糟糕倾向。通常,你可以通过宏实现泛型可以做到的事情(例如,
SortSliceOfInts = MakeSliceSorterFunctionMacro!(int)
可以生成一个新的函数来对整数切片进行排序)。链接:https://docs.julia-lang.cn/en/v0.6.1/manual/metaprogramming/ -
Maxwell Corbin:讨论和开放问题部分提出的问题都可以通过在包级别而不是函数或类型级别定义泛型来避免。原因很简单:类型可以引用自身,但包不能导入自身,虽然有许多方法可以算法化地生成更多类型签名,但不能用导入语句做同样的事情。一个快速的语法示例可能如下:
\\ list package list[T] type T interface{} type List struct { Val T Next *List } // main package main import ( il "list"[int] sl "list"[string] ) var iList = il.List{3} var sList = sl.List{"hello"} // etc...
示例中的语法可能过于冗长,但重点是博客文章中的不幸代码示例都不是合法的构造。包级别泛型避免了元编程中最糟糕的问题,同时保留了其大部分有用性。
-
Andrew Gwozdziewycz:
contract
这个词让我犹豫,因为它会与“Design by Contract”(DbC)中的“contract”概念混淆。虽然泛型的使用场景与 DbC 中的“Contracts”有一些相似之处(如果你仔细看的话),但这两个概念差异很大。由于“Contracts”是计算机科学中的一个既定概念,我认为使用一个不同的名称,如behavior
或trait
,会更不容易引起混淆。设计文档还解释了为什么使用interface
不是理想选择,然而,Go 的 Contract 机制似乎是接口的显而易见扩展,不应如此轻易地忽略……如果可以做到interface setter(x T) { x.Set(string) error }
和interface addable(x T, y U) { x + y }
,它们看起来会非常自然易读。- Russell Johnston:同意合并 Contracts 和接口会很棒。绕过运算符命名问题的另一种方法是提供一些标准的运算符接口,其主体无法在普通 Go 代码中表达。例如,一个标准的
Multipliable
接口将允许*
和*=
运算符,而一个标准的Comparable
接口将允许==
、!=
、<
、<=
、>=
和>
。为了表达具有多种类型的运算符,这些接口可能需要自身带有类型参数,例如:type Multipliable(s Self /* this exists implicitly on all interfaces */, t Other) interface { /* provided by the language */ }
。然后,用户编写的接口/Contracts 可以使用这些标准的基于标识符的名称,从而巧妙地避开设计文档中关于语法和类型的问题。 - Roberto (empijei) Clapis:我同意这一点,也同意应该更清楚在哪里使用接口和在哪里使用 Contracts。统一两者会很棒,因为它们都试图解决重叠的问题。
- Kurnia D Win:我认为
constraint
比contract
是个更好的关键字。我个人喜欢type addable constraint(x T, y U) { x + y }
而不是与接口合并。
- Russell Johnston:同意合并 Contracts 和接口会很棒。绕过运算符命名问题的另一种方法是提供一些标准的运算符接口,其主体无法在普通 Go 代码中表达。例如,一个标准的
-
Hajime Hoshi:我觉得这个提议对于解决我们在https://go.googlesource.com/proposal/+/master/design/go2draft-generics-overview.md 列出的问题来说太庞大了。我担心这个功能会被滥用并降低代码的可读性。抱歉,如果我遗漏了什么,但该提案没有提及
go generate
。go generate
对于解决这些问题难道不够吗? -
Stephen Rowles:我觉得方法语法很难解析,作为人类阅读者,使用不同类型的括号来包围类型部分可能会更清晰,例如:我也是 👍 +1。还有 👍 +1(Pasha Osipyants)。
func Sum<type T Addable>(x []T) T { var total T for _, v := range x { total += v } return total }
-
yesuu:在这个例子中,将
T
视为参数名,将type
视为参数类型。显然,将type
放在后面更合理,然后是 contract,如chan int
。func Sum(T type Addable)(x []T) T
-
Seebs:反馈太长不适合在此处引用,2018 年 8 月。总结基本上是“我希望有一种方法为两种类型分别指定一个 contract,而不是一个 contract 针对两种类型”,以及“我更喜欢
map[T1]T2
而不是t1var == t1var
作为‘T1 必须是允许的 map 键’的规范形式”。 -
Seebs:“如果 contracts 只是类型参数函数呢?”(2018 年 9 月 1 日)
-
Sean Quinlan:我觉得 contract 语法非常混乱。对于一个本应精确定义 api 所需内容并作为其文档一部分的语言特性,它却可以包含各种不影响 contract 的杂乱内容。而且,引用设计文档:“我们不需要解释 contract 主体中每个语句的含义”。这似乎与我从 contract 中期望的相反。一个函数的主体可以复制到 contract 中并正常工作,这对我来说似乎是个 bug,而不是一个特性。我个人更倾向于一个统一接口和 contracts 的模型。接口感觉更接近我想要的 contract 的样子,而且它们之间有很大的重叠。许多 contracts 也会是接口吗?
-
Nodir Turakulov:请详细说明。
像 container/list 和 container/ring 这样的包,以及 sync.Map 这样的类型将被更新为编译时类型安全。
和
math 包将扩展,提供一组适用于所有数值类型的简单标准算法,例如常用的 Min 和 Max 函数。
或者最好是添加一个关于现有类型/函数迁移/更新以使用类型多态的部分。据我所知,向现有类型/函数添加类型参数最有可能破坏使用该类型/函数的现有程序。
math.Max
具体会如何更改?意图是进行向后不兼容的更改,并编写工具来自动将代码转换为 Go2 吗?其他提供当前使用interface{}
的函数和类型的库的作者的一般建议是什么?是否考虑了类型参数的默认值?例如,math.Max
的类型参数将默认为float64
,"container/list".List
的类型参数将默认为interface{}
? -
Ward Harold:为了完整起见,应将Modula-3 的泛型设计纳入其他语言设计部分。Modula-3 是一种优美的语言,可惜上市时机不对。
- Matt Holiday:同样,提及Alphard 语言,该语言大约与 CLU 同时开发,也影响了 Ada 的设计。请参阅 Alphard: Form and Content, Mary Shaw, ed., Springer 1991,其中收集了各种论文和一些辅助材料。Alphard 和 Ada 是我接触泛型编程的入门。Go 能否在等待 40 年后,击败 C++ 最终交付 Contracts?
-
Ole Begemann:您在Generics Overview 页面上写道:“Swift 在 2017 年发布的 Swift 4 中添加了泛型。”这是不正确的。Swift 自 2014 年首次公开发布以来就支持泛型。证据(仅举一例):2014 年 WWDC 的一次 Apple 开发者谈话记录详细介绍了 Swift 的泛型功能。
这也不正确:“
Equatable
似乎是 Swift 的内置类型,无法以其他方式定义。”Equatable
协议定义在 Swift 标准库中,但它没有什么特别之处。在“普通”代码中定义相同的东西是完全可能的。 -
Kevin Gillette:对“Contracts”草案的修正,截至 2018 年 8 月 30 日
check.Convert(int, interface{})(0, 0)
的实例应改为check.Convert(int, interface{})(0)
,或提供为什么该函数应接受两个零而不是一个的解释。 -
Adam Ierymenko:我有一个关于在 Go 中实现有限运算符重载的想法,这可能会使此提案对数值代码更有用。它内容较多,所以我放在了 Gist 中。
- DeedleFake:我完全同意反对运算符重载的论点,并且总的来说我很高兴 Go 没有它,但我同时也认为,无法通过 contract 来解决
a == b
和a.Equals(b)
之间的区别是当前草案设计最大的问题。这意味着对于相当一部分事情,您仍然会编写多个函数。例如,尝试编写一个二叉树。您应该使用带有t < t
或t.Less(t)
的 contract 吗?对于求和函数,您应该使用t + t
还是t.Plus(t)
?我肯定需要一个不涉及运算符重载的解决方案。也许有一种方法可以指定一个适配器,基本上说“如果使用类型 T,它满足 contract A 但不满足 B,并且 T 被用于一个受 contract B 约束的参数,则应用此适配器以使其满足 contract B
”。例如,Contract B 可能需要一个Plus()
方法,而 Contract A 需要使用+
,因此适配器会自动将用户指定的Plus()
方法附加到T
,以使其在该 contract 下使用。- 一个可能对该提案有用的内置函数是
equal(a, b)
,如果存在a.Equals(b)
则使用它,否则使用a == b
,如果类型不可比较(同样适用于其他运算符)则编译失败。这太奇怪了,不能认真考虑,但它将与 contracts 一起使用,并允许在不引入运算符重载的情况下解决具有运算符的类型和无法使用的类型之间的不对称性 —jimmyfrasche - 另一个想法是显式可重载的运算符:
a + b
不可重载,但a [+] b
可重载。对于基本类型,它将使用普通 +,但对于对象(如果存在),它将使用Operator+()
等。我确实认为,没有某种合理的运算符重载或类似机制的泛型用处不大,以至于你不如不进行泛型。-Adam Ierymenko(原始发布者)
- 一个可能对该提案有用的内置函数是
- Ian Denhardt:DeedleFake 很好地阐述了没有运算符重载的问题。我认为涉及使重载“显式”的提案是错误的;相反,我们应该限制哪些运算符可以被重载,以满足这些标准:
- 运算符的语义可以理解为方法调用。大多数数字上的运算符都通过了这项测试;
big.Add
仍然是加法,从 int32、uint64 等的角度来看。未通过此测试的运算符示例是&&
和||
;它们是短路运算符,任何函数或方法都无法复制。无论你怎么看,它们本质上都不是方法,不应被方法覆盖。我认为运算符重载之所以声名狼藉,部分原因在于 C++ 允许你覆盖所有内容,包括像逗号运算符这样的疯狂东西。 - 应该有明确的覆盖它们的使用场景。例如,算术运算符通过了这项测试,以及
<
及其同类运算符。指针解引用通过了第一项测试,但我很难想出“其他类型的指针”的有用的使用场景。它们在 C++ 中有一定的道理,但垃圾回收指针基本上已经涵盖了您的情况。 - 运算符的正常含义应该是容易理解的。例如,指针是错误的常见来源,而
*foo
可能执行除了从内存地址读取之外的操作,这使得本来就很困难的调试会话更加困难。另一方面,+
可能调用big.Add
的可能性相对独立,不太可能造成很大的混淆。 - 最后,标准库必须树立一个好榜样;覆盖
+
的方法在概念上应该是加法,例如。C++ 在定义实际上是os.Stdout.ShiftLeft("Hello, World!")
的内容时,就走上了一条完全错误的道路。
- 运算符的语义可以理解为方法调用。大多数数字上的运算符都通过了这项测试;
- DeedleFake:我完全同意反对运算符重载的论点,并且总的来说我很高兴 Go 没有它,但我同时也认为,无法通过 contract 来解决
-
Eltjon Metko:如何在函数参数内的类型标识符之后指定 contract?这样就可以推断出 T 是什么,我们就可以消除第一组括号。
func Sum(x []T:Addable) T { var total T for _, v := range x { total += v } return total }
-
Tristan Colgate-McFarlane:经过反复权衡,我最终支持该提案(大致按原样)。有限的 contract 语法可能更可取,但我认为它应该允许引用特定的字段(而不仅仅是方法,如一些人提出的)。如果能让兼容的接口和 contracts 之间的互用更容易,那也很好(尽管我认为可能不需要额外的规范)。最后,我认为考虑弃用接口类型是值得的。虽然很激进,但 contracts 基本上也允许指定行为。任何限制这些的 contract 限制(例如引用包内的其他类型),都应该被解除。Contracts 似乎是接口的严格超集,我通常反对存在两个重叠的功能。还应考虑一个辅助编写接口的工具。
-
Patrick Smith:我们可以考虑在定义泛型类型的方法时要求
type
关键字。这会使代码稍微冗长一些,但更清晰、更一致(现在类型参数始终以type
关键字开头)。func (x Foo(type T)) Bar()
-
Patrick Smith:在此示例中,
Foo(T)
是否嵌入在Bar(T)
中,还是Bar(T)
有一个名为Foo
的方法?type Foo(type T) interface {} type Bar(type T) interface { Foo(T) }
-
Xingtao Zhao:提案中有太多的圆括号。在提案中,据说“[]”在某些情况下存在歧义。而如果我们使用 [type T, S contract],则不再有歧义。
-
Dave Cheney:早期的 Type Functions 提案表明,类型声明可以支持参数。如果这是正确的,那么提议的 contract 声明可以重写为:
contract stringer(x T) { var s string = x.String() }
转换为
type stringer(x T) contract { var s string = x.String() }
这支持 Roger 的观察,即 contract 是接口的超集。
type stringer(x T) contract { ... }
以与type stringer interface { ... }
引入新接口类型相同的方式引入了新的 contract 类型。- jimmyfrasche:Contract 不是类型。你不能有一个
stringer
值。你有一个是stringer
的类型的变量。它是一种元类型。类型是对值的谓词。你问编译器“这个值是string
吗?”它回答是(允许编译继续)或否(停止告诉你哪里出错了)。Contract 是对类型向量的谓词。你问编译器两个问题。这些类型满足这个 contract 吗?然后:这些值满足这些类型吗?接口在某种程度上模糊了这些界限,通过存储一个(type, value)
对,前提是该类型具有适当的方法。它同时是类型和元类型。任何不使用接口作为元类型的泛型系统都将不可避免地包含接口的超集。虽然完全有可能定义一个仅使用接口作为元类型的泛型系统,但这意味着失去了编写使用接口无法描述的事物(如运算符)的泛型函数的能力。你必须将你可以询问类型的类型限制为它们的接口集。(我对此没意见)。
- jimmyfrasche:Contract 不是类型。你不能有一个
-
btj:草案设计文档的“其他语言设计”部分缺少两个非常重要的条目:Haskell(带有类型类)和 Scala(带有隐式参数)。
-
iamgoroot:难道不应该自然地提供更好的类型别名支持,并让用户选择泛型吗?而且你不需要太多语法来实现这一点。
type Key _
type Value _
type IntStringHolder Holder<Key:int, Value:string>
type Holder struct {
K Key
V Value
}
func (h *Holder) Set(k Key, v Value) {
h.K = k
h.V = v
}
func main() {
v:= IntStringHolder{}
v.Set(7,"Lucky")
}
-
antoniomo:虽然草案清楚地解释了为什么
F<T>
、F[T]
和非 ASCII(无法在此处输入)F<<T>>
被丢弃,但感觉F{T}
比有时连续三个()
更具人类可读性,同时又不会因无界前瞻而使解析器复杂化,因为在那些情况下你无法打开一个块。 -
aprice2704:我非常不喜欢使用普通括号
(
。一个两字符序列会导致因无界前瞻而产生的编译器开销吗?<|
和|>
怎么样?它们可行吗?它们具有与(
截然不同的优势,在 ASCII 中具有一定的视觉效果,并且在我在 VSCode 中使用的“Fira Code”字体(强烈推荐)中,存在使其渲染为小向右或向左三角形的连字。 -
leaxoy:首先,我很抱歉编辑了页面底部,但我无法删除底部内容。这是我的观点:大量的
(
和)
使 Go 看起来非常混乱,<
和>
就像其他语言一样更好,并且对那些来自其他语言的人更友好。 -
Hajime Hoshi:我完全同意 aprice2704 关于语法的担忧。例如,
[[
/]]
不可行吗?
此内容是 Go Wiki 的一部分。