在主要与 Ruby 合作多年后,我来到了Nanit。那时我并不真正了解 Clojure,所以在我的第一阶段,我主要做 Ruby 工作以提供快速价值。从那以后 6 年多过去了,今天 Clojure 是我工具箱中最强大的工具之一,也是我觉得最高效的语言。
在这些年里,Nanit 的后端团队变得越来越大,关于选择 Clojure 作为我们主要编程语言的问题一再出现,这主要是因为缺乏经验丰富的 Clojure 工程师。
当我试图为这个问题提供答案时,我总是觉得我必须回忆我的想法并将它们组织成连贯的论点,尽管 Clojure 的优势一直对我来说非常清楚。我决定有一天我会在一篇博客文章中表达我对 Clojure 的看法。这一天到了:)
我总是喜欢说,尽管我已经与 Clojure 一起工作了五年多,但我绝不是“Clojure 专家”。因为我认为自己是一个倾向于深入研究主题的人,所以我认为这更多地反映了 Clojure 作为一种语言而不是作为软件工程师的我。只是与其他编程语言相比,Clojure 相当简单,简化一个主题,使专业知识变得深奥。换句话说,Clojure 允许您以很少的知识实现很多,因为要知道的并不多,这真的很棒。
不应将简单与软弱混淆。相反,Clojure 的简单性是它的主要优势,因为您可以实现使用其他语言(如 Ruby、Java 或 Python)所能实现的一切,而代码中的开销和意外复杂性更少。
我想尽量避免“语言战争”,得出一个绝对的结论,即 Clojure 是地球上最好的语言。Clojure 是我工具箱中的另一个工具,可能比其他用例更适合某些用例。相反,我将尝试列出使我在使用 Clojure 时更轻松的客观参数,以及一些我在使用 Clojure 作为语言在技术上遇到困难的主题,以及构建一个主要将 Clojure 作为他们的工具实践的软件工程师团队。
函数式编程
Clojure 是一种函数式编程 (FP) 语言。对我来说,作为一个软件开发者,FPs 最大的优势就是大部分的代码库都是由“纯函数”组成的。纯函数有两个特性,使它们更容易测试、重构和组合成更复杂的函数:
我创建软件分为4个主要活动:
上述两个特征的组合使我更容易列出任何列出的活动:
只有值,只有基元
Clojure 没有“对象”。我的意思是,确实如此,但大多数时候你不会觉得需要这些。相反,Clojure 依赖于原始值和它们的集合(数组、字典、集合等)。我在 Clojure 中所做的 99% 都是使用包含原始值的数组和字典。
作为一名软件工程师,处理原始值对我来说更容易:
最少的语法
Clojure 的语法建立在它自己的数据类型之外。此属性称为[同质性](https://en.wikipedia.org/wiki/Homoiconicity#:~:text=In%20computer%20programming%2C%20homoiconicity%20(from,property%20of%20some%20programming%20languages.&text=Homoiconic%20languages%20typically%20include%20full,programs%20in%20a%20concise%20way.)。起初听起来很奇怪,但我会尝试证明:
Clojure 向量(其他语言中的数组)如下所示[1 2 3 4]Clojure 列表如下所示:(1 2 3 4)
要定义一个函数,你会写:
(defn my-sum [arg1 arg2] (+ arg1 arg2))
如您所见,代码是一个 Clojure 列表,其中包含符号defn、函数名称和参数向量。主体是一个列表,其中函数作为第一个成员 (+),后面是参数。
为什么这是一件好事,你可能会问自己?好问题!
并发
使用 Clojure 时,并发感觉不是问题,主要有两个原因:
Java互操作
Clojure 不是一种广泛使用的编程语言,因此,常见用例缺少许多库。幸运的是,Clojure 与 Java 的互操作是无缝的,因此在实践中,Java 的庞大生态系统触手可及。通过这种方式,您可以享受使用 Clojure 的乐趣,但不会受到其缺乏流行度和库的影响。
缺点
是的,Clojure 很棒,但就像我们在生活中做出的大多数决定一样,使用 Clojure 做出的决定也是权衡取舍。
Clojure 的第一个方面让我过得很艰难是 JVM,原因有以下 3 个:
总而言之,我不是 JVM 的粉丝,但我确实理解将 Clojure 的运行时定位到 JVM 的决定背后的原因。
在大型的、不熟悉的 Clojure 代码库中工作时,我发现困难的第二个主题是Typing.
Clojure 是一种动态语言,它有它的优点,但当我偶然发现一个接收字典参数的函数时,我发现自己花了很多时间来找出它拥有哪些键。有时我不得不在我们的集成环境中放置一个日志,以查看它接收到什么消息以及该消息中有哪些字段可供我使用。
有时我会去测试该函数并查找我们在测试中使用的示例参数值,但这可能还不够,因为该字典中可能存在其他字段并且只是未在函数中使用时刻,因此它们也可能从测试值中丢失。有时我会查看函数的调用站点以了解传递了什么参数以及它是如何构建的。
也有解决方案,例如core.typed,但我自己从未体验过它们,我不确定它们的全面性和可用性。
使用 Clojure 的最后一件难事是招聘和入职,我已经在这篇文章的前面提到过。招聘很难,因为现有的 Clojure 工程师人数很少,而且一些工程师出于职业发展的考虑故意避免使用不受欢迎的语言。其他工程师获得了特定语言的专业知识,并希望继续使用这些语言,因此 Clojure 不是他们的选择。
结论
我认为每个软件工程师至少需要让他们自己熟悉一种函数式编程语言,才能敞开心扉,看看 OOP 范式之外的东西。学习 Clojure 让我怀疑我以前作为软件工程师实践过的一切,并就我如何将精力花在正确的方向上,为我工作的公司提供有价值的基础上提出问题。
我认为 Clojure 作为一种成熟的、可用于生产的、简单的编程语言,非常适合进行这种探索。您可以选择专业地使用它,用于业余项目或根本不使用它,但是让自己接触这种语言的经验肯定会丰富您对编程的看法并使您成为更好的开发人员。