为什么选择Clojure? (2018)

2021-01-04 20:45:58

为什么不使用我已经在使用的编程语言或其他某种语言?是什么使Clojure特别适合现代软件开发?

大部分功能可以通过宏或语言本身将其他语言的扩展名添加为扩展名。多态性,类型,继承,模式匹配和转到通道

出色的并发支持:不变性,软件事务内存," Go Channels" (CSP),代理,JVM / Java中的所有内容

Clojure鼓励您使用不可变的数据结构,而不是对对象进行突变。在许多语言中,您都会创建一个可变数组对象并将其追加。

在Clojure中,每当您添加"到向量(数组),您将得到一个" new"向量和原件不变。凡提及原件的人都可以始终指望原件相同。

这鼓励我们使用函数的组合,而不是使用变异对象的函数。这样做的好处可能不会立即显现出来,但是这种转变鼓励以更简单的方式编写程序。由小型可重用组件组成的程序以后更易于更改。我们只需要关注函数,输入和输出的范围。我们无需担心先前的状态。

在Clojure中,您会注意到的第一件事是有多少个paren和代码有多密集。这需要一些时间来适应,但是parens有很多好处。

我们总是可以用宏重写语法重复,并且有很多减少paren数量的技术,包括" threading" ->>等运算符。以下等同于标题。

Clojure中的函数调用与大多数语言不同。它由一个列表表示,其中第一个元素是函数,其余元素是该函数的参数。

由于包括内置函数在内的所有函数都以相同的方式调用,因此可以轻松地将任何函数与另一个函数(包括内置函数)交换出去。

函数myFunction(arg1,arg2){if(arg1){return arg1; } else {return arg2; }}

Go支持异步" go频道&#34 ;;由于语言中包含特殊语法。 Clojure添加了与第三方库相同的功能和语法。在Javascript中,您必须等待语法被采用或使用翻译器,但在Clojure中,任何人都可以将其实现为库。

Clojure确实擅长从图谱和序列中提取数据。这对于"数据程序"来说真的很不错,因为它们通常调用一个API,转换一个序列并调用另一个API。

(def large-list'(1 2 3 4 5 6 7 8 9 10))(让[[a b c] large-list](str a b c))

(defn configure [val options](让[{:keys [debug verbose]:or {debug false,verbose false}} options](str" val =" val" debug =&# 34; debug" verbose =" verbose))))(配置" foo!" {:debug true})

快速反馈循环对于提高生产力至关重要。刚开始编程时,我会编写一些代码,进行编译,然后手动测试更改(也许使用调试器)。然后,我通过自动测试运行程序发现了TDD,这给了我一个更快的反馈循环,因为我可以确信我的程序可以工作,而不必为每次更改都重新编译。到目前为止,我发现的最快的反馈循环是具有编辑器集成功能的Clojure REPL。使用Emacs和CIDER,我可以在编写代码时在编辑器中执行代码。在编写测试之前拥有用于探索性编码的快速反馈循环有助于我提高工作效率并编写更高质量的代码。其他语言也具有REPL,但是我认为Clojure具有不可更改的功能,因此非常适合此工作流程。

当大多数事物都是映射和纯函数时,测试会更简单。这是来自'Gilded Rose Kata'的示例。

@Test公共无效qualityControl = new();应该从不增加质量到更多的(){backstagePass = anItem()。 withName(BACKSTAGE_PASS_ITEM_NAME)。 build()backstagePass。 setSellIn(FIVE_DAYS); backstagePass。 setQuality(50);质量控制。 updateQualityFor(backstagePass); assertThat(backstagePass。getQuality(),是(50)); }

(def max-quality-pass {:quality 50:sell-in 5})(deftest test-backstage-pass-peak(testing" Quality never not over 50"(is(= 50(:quality(我/更新项目最大质量通过))))))

Clojure与世界上最流行的语言有很好的互操作性。您可以利用Java生态系统来获取诸如AWS开发工具包或数据库客户端之类的基础库。 Clojurescript在React周围有一个很好的包装,称为Reagent。您可以用Clojure编写整个堆栈,这意味着一个人可以非常高效。互操作的故事虽然不是很完美:尽管从技术上讲它是可行的,但是编程模型之间的差异确实存在一些摩擦。这通常可以通过编写包装器来解决。

既然摩尔定律即将结束,我们就不能再依靠单个内核的速度提高了。我们需要编写可以利用多个内核并且可以正确并行运行的代码。我对使用某些语言(例如Python或Javascript)本质上是单线程的感觉不好。诸如Java或C ++之类的语言在设计时并没有考虑到并发性,因此很难正确使用。 Clojure的数据结构默认情况下是线程安全的,并且具有许多并发原语。语言设计不再强调状态的使用,而是强调使用值。

我最初不喜欢来自半强类型Java和C ++的clojure。如果使用类型,则必须考虑它们的缺点以及由流经程序的类型信息引入的耦合成本。使用Clojure之后,我发现类似" builder模式"人为的。通常只有少数几种真实的" data"程序的其余部分是数据的子集和组合,它们并不总是应使用明确的名称或类型。我觉得使用鼓励类的语言会鼓励您过早地进行抽象,而错误地进行抽象比重复要糟糕得多。很多" bug"由于复杂性的增加,您在编译时发现的错误常常是自欺欺人的错误。我认为测试是一种更强大的软件验证形式,它将捕获类型可能存在的错误。

在Clojure中进行了设计选择和折衷。与我使用的任何其他语言相比,Clojure中的锋利边缘和历史事故要少得多。 Clojure对编写软件的方式持坚定态度,但如果您采纳这种观点,则使用纯函数和不可变的数据结构,将使体验大大简化。

我记得我必须学习比较Java中的原语和对象之间的区别。这样的代码感觉很不直观。

一个=新(1); b =新(1);如果(a == b){.out。 println(" True")}其他{.out。 println(" False")}如果(a。等于(b)){.out。 println(" True")}其他{.out。 println(" False")}

比较对象相等性几乎从来不是我想要做的,而且我不认为这是一种很好的默认行为。

这确实符合我的期望,并且我不需要知道=,等于,==,===,deepEqual和deepStrictEqual之间的区别。

对于结构相等,只有=,没有赋值运算符。唯一的假值是nil,其他所有值都是真。

Clojure被描述为具有“点菜”的多态性,这意味着Clojure具有继承和接口的优点,而不必被迫使用它,也没有很多缺点。

在Java中,我们可以执行类似的操作来表示可以在程序其他区域中扩展的形状。

抽象类{public double area(){返回0; }}类扩展{protected int width,height; public(int width,int height){this.width = width;这个.height = height; } public double area(){返回宽度*高度; }}类扩展{protected int a,b,c; public(int a,int b,int c){此.a = a;这个.b = b;这个.c = c; } public double area(){double s =(a + b + c)/ 2;归还。 sqrt(s *(s-a)*(s-b)*(s-c))); }}类扩展{protected int r; public(int r){此.r = r; } public double area(){return .PI * r * r; }}

这种方法的一个缺点是,我们需要提前认真考虑数据之间的关系。

在Clojure中,我们可以从数据开始,并在需要时或在需要时添加多个调度。

(def三角{:shape:triangle:point-a 1:point-b 2:point-c 3})(def rect {:shape:rect:width 3:height 4})(def circle {:shape:circle:半径5})(defmulti区域:shape)(defmethod区域:rect [r](*(:width r)(:height r)))(defmethod区域:circle [c](* Math / PI(*(:radius c)(:radius c))))(defmethod area:default [x]:error)(区域圆)