为什么Go对愚蠢的程序员不利

撰写本文是为了响应先前发布的对立文章


图片


在过去的两年多的时间里,我一直在使用Go来实现带有已开发计费系统的专用RADIUS服务器。 在这个过程中,我学习了语言本身的精妙之处。 程序本身非常简单,并不是写文章的目的,但是使用Go本身的经验值得一说。 对于严肃的,可扩展的代码,Go成为一种越来越流行的语言。 该语言是在Google中创建的,目前已在其中积极使用。 总而言之,我真诚地认为Go语言设计对Dumb程序员不利。


专为弱小的程序员设计?


谈论问题的能力很弱。 强烈谈论想法和梦想...

Go非常简单易学,非常简单,您几乎不需要做任何准备就可以阅读代码。 与非核心专家(经理,客户等)一起阅读代码时,许多世界公司都使用该语言的此功能。 这对于诸如设计驱动开发之类的方法非常方便。


一两个星期后,即使是新手程序员也开始产生相当不错的代码。 我学习Go的书叫做“ Go编程”(由Mark Summerfield撰写)。 这本书非常好,涉及了语言的许多细微差别。 在出现了不必要的复杂语言(例如Java,PHP)之后,魔术的缺乏令人耳目一新。 但是迟早,许多有限的程序员都希望在新领域中使用旧方法。 这真的有必要吗?


语言的主要思想家罗伯·派克(Rob Pike)将Go语言创建为一种易于阅读,有效使用的工业语言。 该语言旨在为大型团队提供最大的生产力,这是毫无疑问的。 许多新手程序员抱怨他们缺少许多功能。 这种对简单性的渴望是语言开发人员的有意识决定,为了完全理解它的用途,我们需要了解开发人员的动机以及他们在Go中的成就。


那为什么这么简单呢? 以下是罗伯·派克的几句话:


这里的关键是我们的程序员不是研究人员。 他们通常很年轻,放学后才来找我们,也许他们学习过Java,C / C ++或Python。 他们无法理解出色的语言,但是与此同时,我们希望他们创建出色的软件。 这就是为什么语言应该易于理解和学习。

他应该很熟悉,大概像C一样说话。 Google程序员很早就开始了职业生涯,并且大多熟悉过程语言,尤其是C族。 对新编程语言的快速生产力的要求意味着该语言不应过于激进。

明智的话,对不对?


朴素的文物


简单是美丽的必要条件。 列夫·托尔斯泰。

在任何设计中,简单都是最重要的愿望之一。 如您所知,一个完美的项目不是没有要添加的项目,而是没有要删除的项目。 许多人认为,为了解决(甚至表达)复杂的任务,需要使用复杂的工具。 但是,事实并非如此。 以PERL语言为例。 该语言的思想家认为,程序员应该至少采用三种不同的方法来解决一个问题。 Go语言的思想家采用了不同的方法,他们决定实现目标,一种方法就足够了,但确实很好。 这种方法有一个坚实的基础:唯一的方法是更容易学习,更难忘记。


许多移民抱怨该语言不包含优雅的抽象。 是的,是的,但这是该语言的主要优点之一。 该语言包含最少的魔力-因此,无需深厚的知识即可阅读该程序。 至于代码的冗长性,这根本不是问题。 编写良好的Golang程序垂直读取,几乎不需要结构化。 此外,读取程序的速度至少比编写程序快一个数量级。 如果您认为所有代码都具有统一的格式(使用内置的gofmt命令执行),那么读取几行额外内容根本不是问题。


不太表达


限制他的自由时,艺术是不能容忍的。 准确性不是他的责任。

由于对简单性的渴望,Go缺乏在其他语言中被习惯的人们理解为自然的结构。 起初,这可能会带来一些不便,但随后您会注意到,该程序被读取多次变得更加容易和确定。


例如,从命令行参数读取stdin或文件的控制台实用程序将如下所示:


package main import ( "bufio" "flag" "fmt" "log" "os" ) func main() { flag.Parse() scanner := newScanner(flag.Args()) var text string for scanner.Scan() { text += scanner.Text() } if err := scanner.Err(); err != nil { log.Fatal(err) } fmt.Println(text) } func newScanner(flags []string) *bufio.Scanner { if len(flags) == 0 { return bufio.NewScanner(os.Stdin) } file, err := os.Open(flags[0]) if err != nil { log.Fatal(err) } return bufio.NewScanner(file) } 

尽管用D语言解决相同问题的方法看起来要短一些,但是,阅读起来并不容易


 import std.stdio, std.array, std.conv; void main(string[] args) { try { auto source = args.length > 1 ? File(args[1], "r") : stdin; auto text = source.byLine.join.to!(string); writeln(text); } catch (Exception ex) { writeln(ex.msg); } } 

复制地狱


人在自己身上下地狱。 马丁·路德

初学者一直在抱怨Go缺乏通用名称。 要解决此问题,其中大多数使用直接代码复制。 例如,这种不幸的专业人员认为,总结整数列表的功能只能通过对每种数据类型进行简单的复制粘贴来实现。


 package main import "fmt" func int64Sum(list []int64) (uint64) { var result int64 = 0 for x := 0; x < len(list); x++ { result += list[x] } return uint64(result) } func int32Sum(list []int32) (uint64) { var result int32 = 0 for x := 0; x < len(list); x++ { result += list[x] } return uint64(result) } func main() { list32 := []int32{1, 2, 3, 4, 5} list64 := []int64{1, 2, 3, 4, 5} fmt.Println(int32Sum(list32)) fmt.Println(int64Sum(list64)) } 

该语言具有实现此类构造的足够方法。 例如,通用编程就可以了。


 package main import "fmt" func Eval32(list []int32, fn func(a, b int32)int32) int32 { var res int32 for _, val := range list { res = fn(res, val) } return res } func int32Add(a, b int32) int32 { return a + b } func int32Sub(a, b int32) int32 { return a - b } func Eval64(list []int64, fn func(a, b int64)int64) int64 { var res int64 for _, val := range list { res = fn(res, val) } return res } func int64Add(a, b int64) int64 { return a + b } func int64Sub(a, b int64) int64 { return a - b } func main() { list32 := []int32{1, 2, 3, 4, 5} list64 := []int64{1, 2, 3, 4, 5} fmt.Println(Eval32(list32, int32Add)) fmt.Println(Eval64(list64, int64Add)) fmt.Println(Eval64(list64, int64Sub)) } 

而且,尽管我们的代码比以前的情况要长一些,但它已被推广。 因此,对我们来说,实现所有算术运算并不困难。


许多人会说D程序看起来很短并且是正确的。


 import std.stdio; import std.algorithm; void main(string[] args) { [1, 2, 3, 4, 5].reduce!((a, b) => a + b).writeln; } 

但是,它只会更短,但不会更正确,因为在D实现中完全忽略了错误处理问题。


在现实生活中,当逻辑的复杂性增加时,差距正在迅速缩小。 甚至更快,当需要使用标准语言运算符无法执行的操作时,差距会缩小。


在可支持性,可扩展性,可读性方面,我认为,尽管Go语言失去了冗长性,但它是赢家。


在某些情况下,通用编程会给我们带来不可否认的好处。 这清楚地说明了排序包。 因此,对任何列表进行排序,对于我们来说,实现sort.Interface接口就足够了。


 import "sort" type Names []string func (ns Names) Len() int { return len(ns) } func (ns Names) Less(i, j int) bool { return ns[i] < ns[j] } func (ns Names) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] } func main() { names := Names{"London", "Berlin", "Rim"} sort.Sort(names) } 

如果您采用任何开源项目并运行grep“ interface {}” -R命令,您将看到使用混乱接口的频率。 胸襟狭窄的同志们会立即说,这一切都是由于缺乏仿制药。 但是,事实并非总是如此。 以DELPHI语言为例。 尽管具有这些相同的泛型,但它包含一个特殊类型VARIANT,用于对任意数据类型的操作。 去做同样的事情。


从麻雀上的枪


紧身衣必须适合疯狂的大小。 斯坦尼斯拉夫·莱茨。

许多极限运动爱好者可以说Go具有创建仿制药的另一种机制-反射。 他们是正确的……但仅在极少数情况下。


Rob Pike警告我们:


这是一个功能强大的工具,应谨慎使用。 除非绝对必要,否则应避免使用它。

维基百科告诉我们以下内容:


反射是程序可以在运行时跟踪和修改其自身的结构和行为的过程。 反映反射的编程范例称为自反编程。 这是一种元编程。

但是,如您所知,您必须付出一切。 在这种情况下,它是:


  • 编写程序困难
  • 程序执行速度

因此,作为大口径武器,应谨慎使用反射。 漫不经心地使用反射会导致程序无法读取,不断出现错误并降低速度。 唯一的问题是,势利的程序员可以在其他更务实和谦虚的同事面前炫耀自己的代码。


来自C的文化包?? 不,来自多种语言!


除了国家,继承人还欠下了债务。

尽管许多人认为该语言完全基于C的遗产,但事实并非如此。 该语言结合了最佳编程语言的许多方面。


句法


首先,语法构造的语法基于C语言的语法。 但是,DELPHI语言也产生了重大影响。 因此,我们看到多余的括号已被完全删除,从而大大降低了程序的可读性。 此外,该语言还包含运算符:: =,这是DELPHI语言固有的。 包的概念是从ADA之类的语言中借用的。 未使用实体的声明是从PROLOG语言中借用的。


语义学


DELPHI语言的语义作为软件包的基础。 每个包都封装数据和代码,并包含私有和公共实体。 这使您可以将包接口减少到最小。


委托方式的实现操作是从DELPHI借来的。


合编


难怪有一个玩笑:Go是在编译C程序时开发的。 该语言的优势之一是超快速编译。 这个想法是从DELPHI借来的。 另外,每个Go包都对应于DELPHI模块。 仅在必要时重新编译这些软件包。 因此,在下一次编辑之后,不必编译整个程序,而仅需重新编译已更改的程序包和依赖于这些已更改的程序包的程序包(并且仅在程序包接口已更改的情况下)就足够了。


高级设计


该语言包含许多不同的高级构造,这些构造绝不与C等低级语言关联。


  • 线数
  • 表哈希
  • 切片
  • 鸭式打字是从RUBY之类的语言中借来的(不幸的是,许多人不理解也不充分利用它们的潜能)。

记忆体管理


内存管理通常值得一提。 如果在像C ++这样的语言中,控制权完全留给了开发人员,则在像DELPHI这样的较新语言中,使用了引用计数模型。 使用这种方法,由于形成了丢失的簇,因此不允许循环链接,因此Go内置了对此类簇的检测(如C#中那样)。 另外,垃圾收集器比大多数当前已知的实现更有效,并且已经可以用于许多实时任务。 语言本身可以识别以下情况:可以在堆栈上分配用于存储变量的值。 这样可以减少内存管理器上的负载并提高程序速度。


并发与竞争


语言的并行性和竞争力令人称赞。 没有任何底层语言甚至可以与Go语言进行远程竞争。 公平地讲,值得注意的是,该模型不是由该语言的发明者发明的,而只是借鉴了旧的ADA语言。 该语言能够使用所有CPU处理数百万个并行连接,而对于具有死锁和竞争条件的多线程代码复杂问题,这种语言的通用性要小一些。


其他好处


如果这是有益的,那么每个人都会变得无私。

该语言还为我们提供了许多无疑的好处:


  • 构建项目后唯一的可执行文件大大简化了部署应用程序。
  • 即使不编写测试,静态类型和类型推断也可以显着减少代码中的错误数量。 我知道有些程序员根本不需要编写测试,而同时他们的代码质量也不会受到很大的影响。
  • 非常简单的交叉编译和出色的标准库可移植性,极大地简化了跨平台应用程序的开发。
  • RE2正则表达式是线程安全的,并且具有可预测的运行时。
  • 功能强大的标准库,使大多数项目都无需第三方框架即可完成。
  • 该语言功能强大,足以专注于问题,而不是解决问题的方法,同时又足够低,无法有效解决问题。
  • Go eco系统已经包含了适用于所有场合的现成工具:测试,文档,包装管理,功能强大的短毛绒,代码生成,竞赛条件检测器等。
  • Go 1.11版现在具有基于流行的VCS主机的内置语义依赖性管理。 组成Go生态系统的所有工具都使用这些服务来一口气下载,编译和安装其中的代码。 太好了。 随着版本1.11的问世,软件包版本控制的问题也得到了彻底解决。
  • 由于该语言的主要思想是减少魔术,因此该语言鼓励开发人员显式处理错误处理。 这是正确的,因为否则,他将完全完全忽略错误处理。 另一件事是,大多数开发人员故意忽略错误处理,而是选择简单地转发错误而不是处理错误。
  • 该语言未实现经典的OOP方法,因为Go中没有纯形式的虚拟性。 但是,使用接口时这不是问题。 没有OOP可以大大减少初学者的入门障碍。

轻松获得社区利益


复杂化很简单,简化很难。

Go的设计既简单又出色。 它是为精明的程序员编写的,他们了解团队合作的所有优点,并且厌倦了企业级语言的无尽变化。 由于其语法库中的语法结构集相对较少,因此它实际上不会随时间而变化,因此开发人员已经腾出了很多时间专门用于开发,而不是无休止地研究语言创新。


公司还具有许多优势:入门门槛低,使您可以快速找到专家,并且语言的不变性使您可以在10年后使用相同的代码。


结论


大脑袋尚未使一头大象成为诺贝尔奖获得者。

对于那些个人自我压倒团队精神的程序员,以及热爱学术任务和无休止的“自我完善”的理论家来说,这种语言确实很糟糕,因为它是一种通用的工匠语言,无法从工作成果中获得美感并展现自我一位在同事面前的专业人士(前提是我们使用这些标准而不是使用IQ来精确地测量思想)。 就像生活中的一切,这都是个人优先事项。 像所有有价值的创新一样,语言已经从普遍拒绝到大众认可已经走了很长一段路。 语言的简单之处在于巧妙,但是,正如您所知,一切巧妙之处都是简单的!


总结


在针对Go的所有严厉批评中,以下陈述尤其突出:


  • 没有泛型。 如果我们查看最受欢迎的语言的统计数据 ,则会发现前十种语言中有一半没有泛型。 通常,仅在容器中需要泛型。 因此,从中获得的收益不会太大。
  • Rust之类的其他语言则更好(至少在XXX网站类别中)。 同样,如果我们查看最受欢迎的语言的统计信息 ,则我们根本不会在列表中找到Rust,或者它会在评级之下。 就个人而言,我喜欢Rust,但我选择了Go。
  • XXX有这样的面包。 这就是简单性的反面。 是否不利是每个人的决定。 但是,该项目的开发人员更喜欢简单性。
  • 他们将发布Go 2.0,然后我们将看到。 该职位由观察员而不是从业人员担任。
  • 不够表达。 我同意,在某些方面,表现力是la脚的,但总的来说,它是一种简单而一致的语言。 另外,由于语言的贫乏,我们被迫更多地关注已开发应用程序的体系结构,这对它的灵活性产生了积极影响。

实际上,本文并未考虑Go语言的语法优势,而只是简要概述了Go语言在团队合作中的优势以及正在开发的项目的有效发展。 可以理解,关于更具体的问题,该条将继续进行。 但是,由于对该主题缺乏兴趣,因此很可能无法继续。


实验


不要相信这些话-既不是你的也不是陌生人,而是要相信行为-既是你的也是陌生人。

最后一部分专门针对那些认为自己具有建设性乐观态度并且可以通过自己的事情确认这一点的人。 其他观众,请跳过这一部分。


这项实验的灵感来自于那些朋友,他们声称所有建设性的乐观主义者早就离开了(至少实际上是)我们国家的广阔地区,并定居在例如Stack Overflow上,而这里大多数的势利者仍然存在。 很长一段时间我都不相信他们,所以我决定进行这个实验。
在枢纽上发布了几篇文章,这些是我引用的评论的分析结果。


  • 的确,我朋友的假设得到了证实,但是,尽管小贩的比例正在迅速下降,但他们仍能找到足够的人。 尤里·拜科夫(Yuri Bykov)称这种人为“ 傻瓜 ”,整个国家赖以生存。 据他说,他们所占的比例很小(约2%)。 我不是那么悲观,我认为还有更多。
  • 媒体法。 破坏性信息比建设性信息更具吸引力。
  • 人群的心理。 这是一件可怕的事情,它甚至使一个适度的人成为一个可恶的羊。 人群中的男人不再是男人。 不能谈论任何客观性。 没有逻辑上的论点,权威的来源或判例都不会影响他。
  • 责任与有罪不罚。 人们愿意屈辱他人(至少在自己的眼中)。 特别是如果您不需要回答(这可能会更容易-单击减号,甚至不需要写评论)。 言行之间的共同点与渠道和下水道之间的共同点是一样的。
  • 虚荣心。 大多数势利者随时准备以任何方式脱颖而出。 他们不惧怕任何道德障碍。
  • 悲观主义 与西方国家(尤其是美国)不同,悲观情绪在该国盛行。 如您所知,乐观主义者在困难中寻找机会,悲观主义者在机遇中寻找困难。 在我们国家,几乎没有人关注任何事物的积极品质。
  • 专业精神和世界观。 大多数人选择工具本身就是目的,而不是手段。 人们已经忘记了如何使用信息。 人们看不到树后的森林。 他们无法从一系列信息中提取主要思想。 没有人愿意从一种不同的,不是自己的标准的观点来看待。 异议被压制。 这里不接受。
  • 友善和尊重。 赞美的友善团体仅以言语存在。 敏捷开发价值只是纸上谈兵。
  • 虚伪的。 通常,您可以撰写有关此内容的另一篇文章。
  • 原理 有人问正确的问题:“ 我到底在做什么? ”,但是,并非所有人都知道,由于缺乏原则,对我们而言,短暂的自私利益比我们所有原则的总和更为重要。 最容易将一切归咎于环境,并说没有任何事情取决于我们。

对所有建设性的乐观主义者表示深切的敬意和同情。


Adverax

Source: https://habr.com/ru/post/zh-CN448932/


All Articles