按照惯例,第一篇博文总要说点历史,对吧?我作为软件开发人员,主要还是使用 PHP 语言。第一次接触它还是我在高中的时候,跟一个朋友一起开发一个项目。我们想为项目建一个网站,正好发现一个用 PHP 语言,代码写得非常整齐,并且预打包的解决方案。那个时候,我完全不懂这些代码是干嘛的,也没有花时间去弄懂它。多年之后,我再次与 PHP 相遇。在我进入大学的前 6 个月(我想大约 7 年前),PHP 成了我主要关注的语言之一。两年前,我第一次经人介绍了解到 Go 语言。
我大部分专业经历都来自于位于利兹中心的代理机构。这是一个梦幻般的公司,我从中学到了很多,因为很早我就被给予了很高的自由度。我为一个范围很广的项目工作:标准的 CMS 构建,定制 API 接口,手机应用程序,为了提高效率的自动化,部署和基础架构设计,以及其他很多功能。
在代理机构的这段时间里,我和一个我们聘请的实习生成为了朋友。他真的是个天才,也是他突然向我介绍 Go 语言。我说我会试试,然后说一下我对它的看法。很快,我就觉得我喜欢上它了。跟 PHP 相比,更好的性能,类型安全,支持并发,本地编译后的二进制文件,相当小,启动速度相当快,这非常地诱人!如果你从 PHP 转 Go 开发,那么会有一大堆让你感到激动的东西。
奇怪的是,我其实并没有那么喜欢它。我认为它的语法很怪,它的工具还好,但处理错误是如此地繁琐,(甚至,)天哪,地球上怎么还有像 Go 语言这样没有支持泛型的语言?我记得与这位实习生进行了很多类似的对话,他可能告诉我所有我后来自己也慢慢意识到的事情,但是我继续寻找新的东西来学习 PHP 以外的东西,现在改为 Go 语言也是这样。
使用 Scala 的一年(The Year of Scala)
我的搜索很快把我带到了 Scala 的世界。如果你没怎么听过 Scala 的话,它是运行在 JVM 上的函数编程语言。当时很有吸引力的一点是 JVM 很快(一旦启动并运行起来之后),函数式编程当时疯狂地席卷了编程界。
我迷上了 Scala,花了一年时间围绕着它,学习它的语法。当我爱上它时,尽管编译时间比 Go 长,但使用 SBT 的工具实际上也没有那么糟糕(得益于增量编译)。我学习其语法,花了一些时间之后,就能像编写 Java 一样编写 Scala,而且使用的语法还更少一些。接下来,我专注于学习函数编程原理,如更纯粹,引用透明性,不修改状态(等函数式编程的原则)。在编写 PHP 时,我曾遵循过许多这里描述的原则,但并没有想过要如何编写一个纯函数式的应用程序。
在我使用 Scala 的过程中,我大量阅读了相关博客,阅读了一些 Scala 社区里比较流行的书,参加 Martin Odersky 使用 Scala 函数式编程的课程。我觉得我不能只是待在我能做出有用的东西的地方。我应该待在可以提高在 Scala 项目工作的机会的地方,我对自己的 Scala 技能应该有足够的信心,我应该能为这样的项目工作。然而情况却是…我并不这么觉得。我还是觉得其他人的代码很让人迷惑,很难阅读。觉得其他人写的每一个库都有自己不同的(编程)风格,并且还需要学习大量的词汇。我了解一些广泛被使用的库,像 cats 和 shapeless,但还是不明白它们干了什么或者为什么你需要它们。
回过头来,不要误解我的意思。Scala 是一门令人印象深刻的语言,我非常尊重那些使用这门语言工作的人。它本身没有任何限制,这是好事也是坏事(对于我来说,是坏事)。我喜欢类似于保持不变的想法,难以置信的是编译器捡起了类型错误。这非常罕见,它丢掉了重要的东西,并让错误可以在运行时发生。
我确实用 Scala 做了一些事情,但也弄得我越来越沮丧。我曾想解决我想解决的问题,但总是因为各种原因弄得焦头烂额。要么就是我缺乏函数编程的经验技巧,要么就是像 JVM 启动时间这样的事情让我感到沮丧。不知道为什么,我在这一年后仍然发现还有 Scala 的新语法(需要我去学)。我觉得我已经在这条路上走了一年,但收获甚少。
重回 Go 语言(Back to Go Again)
在使用 Scala 一年以后,我决定再给 Go 一个机会。(因为)我发现没有其他的语言能像 Go 一样在方框中打这么多勾,使用 Scala 的一年只是让我增加更多的方框来打勾。这些方框都有什么呢?
- 快速编译:虽然 SBT 增量式编译在开发时可以加快编译速度,从头开始编译还是需要花费很长的时间,而且即使是 SBT 增量式编译,花的时间也比
go build
花的时间长。 - 快速启动应用:我对于开发 CLI 工具非常感兴趣。早期使用 Go 和 Scala 语言开发的库中都有一个为了创建 CLI 应用的库。这对于用来测试不同语言开发的应用的启动速度非常有用。
- 低内存占用:我并不是研究 JVM 直接使用多少内存的狂热粉丝。但我也知道如果我想将我做的事情 Docker 化,对于今天的 JVM 来说还是个问题。
- 功能丰富,风格一致,但简单的标准库:Scala 拥有大量的库,但你可以发现(其实)大量依赖于 Java 库。有一些并不是那么令人愉快。最重要的是,当你开始引入这些依赖关系时,心里就会开始觉得(为啥)你不得不下载整个互联网来做一些非常基本的事情。
- 编译时类型检查:Scala 在这方面表现出色,但多数情况下,你需要牺牲可读性作为代价。在某些情况下,Go 会漏掉一些编译时的类型检查,但这种情况非常罕见,因为通常有其他可以在编译时做类型检查的解决方案。
- 垃圾收集和类型安全:我不想接触太低层的东西,但我还是想比我使用 PHP 时能对应用程序干什么有更多的控制。
- 内嵌的并发原语:来自于 PHP,这是我想更多解释的地方。使用 Scala 开发程序时,我有机会用上并发,但从来没有像使用 Go 一样对应用程序能控制得这么多。
- 广泛采用的编程风格约定:来自于 PHP,PHP 标准建议(PSR)相当流行,我知道我喜欢拥有能够到处使用的代码风格约定的语言。我相信很多语言都能从中受益,包括 Scala。
对于我来说,Go 符合所有这些要求,甚至更多。我认为 Go 给了你很多可以开箱即用的东西。你也可以在其他地方找到这些东西,但并不像 Go 一样全,而且整洁。
开始激怒我的一些 Go 的特性也变得可以接受了。我觉得我最终明白了一些东西,像 Go 语言一样,也需要作出一些妥协;以确保像上面的那些框可以被打勾。为了你可以得到更多的好处,另外一些地方就得难受一点,但有一点非常清晰,语言总体上被设计成最大化开发生产效率。错误控制就是个很好的例子。它很繁琐,有时它有一点不是那么有用。然而,它让函数里面哪里会发生错误变得非常清晰,这意味着,当你阅读别人代码的时候,你能够清楚地知道什么时候,或者为什么错误会发生。
另外一个可以做出这个判断的,开始时令我感到困惑的是 context
包。我本来并不是很喜欢它,并且认为它处理问题用了一种非常冗长的方式。为什么我们不能直接杀掉 goroutines 呢?context
跟错误非常像,它的冗长也是显式的,告诉你某个给定代码的某些内容很可能在某些时候被取消。你不能杀掉 goroutine,因为你没办法处理它产生的 goroutine?毕竟他们不再是孩子。
约定,工具使得 Go 语言开发的项目令人难以置信地一致。到当前为止,比我用过的任何语言都要好。当你有像 goimports
和 gometalinter
一样的工具,就不奇怪每一个项目都有相同的代码风格,甚至相同的文档风格。工具也能帮助你避免代码中相同的 bug 和减少误解。
我开始用 Go 写软件后,觉得最棒的是,我可以真正解决问题了。我想解决的实际问题,不仅仅是一些随机的数学教学问题。最近一段时间(我希望可以写得很快),我在制作一些工具来帮助自动化我的 Arch Linux 桌面的一些地方。我写了一些工具去管理我的工作区,为一些东西显示通知,做一些像自动配置我的显示器(都是一些解决桌面环境的问题,但它是一切工作的开始)。
学习 Scala 时,对于一些数学问题,我建议创建的解决方案非常有意思,但却(纠结于)没能写出解决实际问题的软件,直到使用 Go 语言才释然。这不是 Scala 的错误,它确实能够解决问题。我知道这是一个巨大的范式转变,鉴于我的历史,我将需要一个很大半径的弯曲学习曲线。对于我来说 Go 没有学习门槛;实际上 Go 本来压根就没有学习门槛。没有很复杂的编译工具,语法可以一下子装进你的脑里,你能很快上手。可能你从第一天开始就可以阅读别人的代码,包括阅读标准库中的代码。
Go 语言不是完美的,但它在不断完善中。我对这门语言的未来感到非常兴奋,并且从中看到了我想要的东西。
via: https://www.elliotdwright.com/2018/02/26/there-and-back-again/
作者:Elliot Wright
译者:gogeof
校对:polaris1119