Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's with clojure's long files?

Tags:

clojure

I've been learning clojure for some weeks and recently I began reading some open source code: clojure and clojurescript compilers and some libraries like om, boot, figwheel.

I noticed some clojure files are very long, some of them more than a thousand LOC. Given that clojure's code is very terse and low ceremony, that code means much more code than a file that big in some other languages.

Coming from an OO background where you usually have one class per file and you try to keep your classes short (SRP) I found that a little weird.

I know that clojure code is mostly composed of pure functions and they're way easier to reason about than some mutable class where you need to keep the current state in mind, and I find that I can read and understand most of the functions one at a time. But most of those functions are very well designed so that they don't depend on each other: even though you can use (filter odd?) it doesn't mean that filter and odd? are related. But for "every day" code (LOB apps, web apps, etc) is very hard to keep the functions as self contained as those (at least that's my experience with OO programming).

I've also seen some demos of clojurescript applications (om, reagent, etc) where they declare all components in the same file. I don't know if that's because it's just a demo and in a real life application you'd have a product.clj and a category.clj or that's just the clojure way: to have one file per namespace/module/bounded-context.

I think that if I open a folder and I see product.clj, category.clj, order.clj, etc I can get the idea at a glance what's that folder about, better than just having a components.clj or core.clj.

So, my questions are:

  1. Is it common for "every day" clojure code to have these very long files? or is it just because I'm reading libraries code and "normal" code is more "modular", I mean: more files and less length.
  2. Does having long files like those actually make it harder to comprehend at a glance what's the application about? like my product/category/order example above, or by some clojuresque property that's not an issue.
  3. In case long files are the "clojure way", how do you handle conflicts, refactorings, programming in a team... if everybody is touching the same file?
like image 282
Orlando William Avatar asked Oct 16 '15 23:10

Orlando William


2 Answers

1: I looked at the reasonably large non-library clojure project i'm working on right now and ran this:

ls **/*.clj | xargs wc -l | awk '{print $1}' | head -n -1 > counts

and opend a repl and ran

user> (float (/ (reduce + counts) (count counts)))
208.76471

I see that on a project with 17k LOC our average clojure file has 200 lines in it. I found one with 1k LOC.

2: Yes, I'll get started breaking that long one down as soon as I have free time. some very long ones such as clojure.core are very long becaues of clojure's one pass design and the need to self-bootstrap. they need to build the ability to have many namespaces for instance before they can do so. For other fancy libraries it may very well be that they had some other design reason for a large file though usually it's a case of "pull request welcome" in my expierence.

3: I do work in a large team with a few large files, we handle merge conflicts with git, though because changes tend to be within a function these come up, for me, much less often than in other languages. I find that it's simply not a problem.

like image 184
Arthur Ulfeldt Avatar answered Sep 23 '22 11:09

Arthur Ulfeldt


  1. They tend to get long as you develop them. Say you need a function foo to do procedures [a b ...] on datastructure K. You first (def) the signature of the function and continue to implement helper functions a b ... since they're likely all pure functions and the functionality you need of foo is complex the namespace tends to get long.

  2. Sometimes, but the repl is a really useful tool, to understand a new library's main functionality I often use clojure.repl/source on the function and work my way backwards on it's helper functions. I find that a lot of time Clojure libraries documentation is either cryptic or non existent, but as many in the community like to say Clojure's functions' source is self documenting.

  3. I have no experience working in a large team, but Arthur Ulfeldt is right most changes happen in a single function, I gather it from reading the diffs of pull requests with Github's Blame feature.

like image 1
Ricardo Acuna Avatar answered Sep 20 '22 11:09

Ricardo Acuna