我已经学习了几个星期的clojure,最近我开始阅读一些开源代码:clojure和clojurescript编译器以及一些像om,boot,figwheel这样的库。
我注意到一些clojure文件非常长,其中一些超过一千个LOC。鉴于clojure的代码是非常简洁和低级的仪式,该代码意味着比其他语言中的大文件更多的代码。
来自OO背景,你通常每个文件有一个类,并且你试着保持你的课程简短(SRP)我发现有点奇怪。
我知道clojure代码主要由纯函数组成,并且它们比一些可变类更容易推理,你需要记住当前状态,我发现我可以阅读和理解最多一次一个的功能。但是大多数这些功能设计得非常好,以至于它们不相互依赖:即使你可以使用(filter odd?)
,它也不代表filter
和{{1} } 有关系。但对于"每天"代码(LOB应用程序,网络应用程序等)很难将这些功能保持为自包含(至少这是我对OO编程的体验)。
我还看到了一些clojurescript应用程序(om,试剂等)的演示,他们在同一个文件中声明了所有组件。我不知道这是不是因为它只是一个演示而且在现实生活中我只有odd?
和product.clj
或者#&# 39; s只是clojure方式:每个命名空间/模块/有界上下文有一个文件。
我认为,如果我打开一个文件夹并看到category.clj
,product.clj
,category.clj
等,我可以一眼就知道该文件夹的内容,比拥有order.clj
或components.clj
更好。
所以,我的问题是:
答案 0 :(得分:5)
1:我查看了我现在正在进行的相当大的非图书馆clojure项目,并运行了这个项目:
ls **/*.clj | xargs wc -l | awk '{print $1}' | head -n -1 > counts
并打开一个repl并运行
user> (float (/ (reduce + counts) (count counts)))
208.76471
我看到在17k LOC 的项目中,我们的平均clojure文件中有200行。我发现一个有1k LOC。
2:是的,一旦我有空的时间,我就会开始打破那个长的。一些非常长的,例如clojure.core是非常长的因为clojure的一次通过设计和自我引导的需要。他们需要建立能够拥有许多名称空间的能力,然后才能这样做。对于其他花哨的图书馆,很可能他们有一些大型文件的其他设计理由,尽管通常它是一个"拉请求欢迎"在我的expierence。
3:我在一个拥有几个大文件的大型团队中工作,我们处理与git的合并冲突,但是因为这些变化往往是在一个函数中出现,对我来说,比其他语言更少。我发现这根本不是问题。
答案 1 :(得分:1)
当你开发它们时,它们往往会变长。假设您需要一个函数foo来对数据结构K执行过程[ab ...]。首先(def)函数的签名并继续实现辅助函数ab ...因为它们可能是所有纯函数和foo所需的功能很复杂,命名空间往往会变长。
有时,但是repl是一个非常有用的工具,为了理解新库的主要功能,我经常在函数上使用clojure.repl / source,然后按照它的方式向后工作辅助功能。我发现很多时候Clojure图书馆的文档要么含糊不清要么不存在,但社区中的很多人都喜欢说Clojure的功能'来源是自我记录。
我没有在大型团队工作的经验,但是Arthur Ulfeldt是正确的,大多数变化都发生在一个函数中,我通过使用Github的Blame功能阅读pull请求的差异来收集它。
答案 2 :(得分:1)
答案 3 :(得分:1)
除了其他人给出的答案外,还有两个答案。
可能有些文件很长,因为在Clojure中,每个命名空间使用一个文件最直接,所以如果你想在同一个命名空间中使用所有这些定义,那么它就是更容易将它们放在一个文件中。希望定义驻留在同一名称空间中的一个原因在#2中给出。
Clojure编译器不允许在命名空间之间使用某些类型的循环依赖关系(命名空间之间的其他循环依赖关系很好)。避免非法循环依赖的一种方法是将相互依赖的定义放在同一名称空间中。如果您这样做,那么将属于有问题的定义的其他定义也引入单个命名空间可能是有意义的。有关此答案的其余部分,请参阅#1。
(我自己的口味是针对几个较小的文件,虽然不像许多Java类文件那么小。另外:代码通常不像作者所想的那样自我记录。即使作者和阅读代码以后是同一个人。)