Clojure at a Bank – Clojure Code Immaturity
pitheringabout.com
pitheringabout.com
I'm not sure why the team used single-letter names. Using non-descriptive arg and function names (if he did that too?) is about as anti-lisp as it gets.
I wonder if this blog, and other blogs like this, do a disservice to the growth of Clojure. Not only is it apparent that using a lisp and immutability is completely alien to the teams adapting it, they seem to take a very long time to ramp up into it, and the conclusion is always the same: they are still slipping and sliding, but at least they aren't breaking their noses anymore. At the same time, these blogs are informative of what companies are likely to get into and it should help stymie the surprises and perhaps keep the noise down for a while, so maybe it helps with growth by attracting those that are really willing to hack it. The author, as most are, is forthcoming with the early mistakes and surprises. This is most certainly to be expected when transitioning to any language, but it doesn't seem like there are many languages where bloggers feel inclined to dote on the initial shock outside of Clojure. It is a wonder that they don't complain about the lack of frameworks a'la RoR/Django and other things one would figure they'd want before diving in. Was it the lack of structure that made them go for it in the first place?
I find myself doing this quite a bit. I think the reason is that it's so pervasive in the core.clj code. Take the documentation for update-in below:
(update-in m [k & ks] f & args)
'Updates' a value in a nested associative structure, where ks is a
sequence of keys and f is a function that will take the old value
and any supplied args and return the new value, and returns a new
nested structure. If any levels do not exist, hash-maps will be
created.
After having looked at a lot of clojure doc, those single letter names make sense to me (k = key, ks = keys, f = function). I use the same style when writing "core-like" functions, but anything remotely application specific is descriptively named.Of course, he didn't show us any of the funky code he was talking about, but I get this impression from the line about using single letter names instead of highly descriptive names.
I should also take the time to look at the Clojure Core one day. Thanks for reminding me to put that on my to-do list.
1. Bootstrapping. Some functions are defined somewhat verbosely because the higher order functions are defined later.
2. Performance. By definition, everyone uses core, so core is on everyone's critical path. Therefore, core must sacrifice idiomatic code and brevity for performance. You should make the same tradeoffs only after measuring.
3. Evolution. Clojure has gained features and, since stability is valued, core doesn't get revamped to utilize them without greater justification.
This is a good point and keeps happening to me. Otoh, there are a lot built in (and so a lot not to reproduce accidentally). clojure/core has > 500 symbols in it[1]. Is there a systematic way to learn what is 'built-in' to Clojure vs waiting till one stumbles across specific items?
[1] (count (ns-publics 'clojure.core)) --> 580
(on the version of Clojure on my machine -> 1.4.0)
[1]: http://clojuredocs.org/quickref/Clojure%20Core
[2]: http://4clojure.com/
No it isn't, because all the basic things needed are there ... Ring, Compojure, ClojureQL, etc... If those libraries where missing, then you'd see people complaining.
On the other hand this does highlight the advantage of using Clojure. Being a pragmatic functional programming language, it's easy to work with and combine small and simple modules, instead of needing a framework that includes the kitchen sink.
There's no good clojure web framework for the fat stuff rails is good at, and it doesn't have the same level of plugin maturity, not by a long shot. That's years away. Clojure's good as an API server for a RIA, but it's probably less productive than rails for many use cases. The tools just aren't there, it takes years for tools to get that good. While the java integration is good, using clojure as java glue isn't so fun.
Clojure's strengths are speed, safety via immutable data, and being concise. It's great for a web server if you legitimately have a case where rails is too slow (you probably really don't though). Clojure's also great for non-web programming tasks.
The thing I experienced is this - once you outgrow the primary use-cases for a monolithic framework, all of that nice packaging and conventions and modules that play well with each other - start getting in your way, soon to be replaced by custom implementations of whatever you need and all that time you saved initially will be extra time you have to spend to strip out those functionalities, in addition to the time take for building those same things from scratch.
I also worked with Rails btw - for instance you can find like a gazillion plugins for authentication and yet none of them did what I wanted on one project I had, so I had to implement my own authentication logic, after spending hours trying to tweak such a plugin first. That same project needed storage of files on S3, including pictures. Again, there are like a gazillion plugins for that, but none of them did what I wanted. So again, I had to implement it by myself. After trying endlessly to tweak Paperclip or others.
For this project I'm working on the admin interface was initially bootstrapped on top of Django's Admin. Which eventually became a liability and had to be replaced completely. The Django's ORM is also becoming a liability - because it couldn't handle queries for complex scenarios that were unrelated to "objects", so right now the project also makes use of SQLAlchemy - and I'll probably have to pull Django's ORM completely from the project, because the project is also doing sharding right now and I want to expand that to most tables and I can't work with a connection object that's a singleton, not to mention that the "objects" exposed will end up being completely different than the schema used in the DB table, so the object-mapping that Django's ORM does will stay in my way.
In the end, if I need to sum up the things that are still from Django in this project, there are surprisingly few things left.
So if you think about it, for small projects frameworks like Django/Rails are too heavy and for large projects they kind of get in the way and it's better off to start from scratch, importing components as needed, because that way you'll build abstractions that are suited for your domain and that will last longer.
Here are the first two posts that discuss their decision:
I also made the newbie mistake of writing simple hash mapping functions that were already well implemented. The problem is coming from Ruby,/Java you're not intuitively aware of the standard functions compared to going from one OO language to another.
I used to be in the "code tells a story, no comments needed" camp. In large part because two of my three university courses had automatic marking system which detracted points if comments exceeded a certain percentage of text mass. Habits!
Gradually I've been finding myself commenting more and more in my code. In FP (mostly F#) code I almost always end up with some incredibly information dense set of lines that needs at least some defense of its existence.
Having spent some recent weekends reading the sources of id-software games[1] I've started to really think about the approach to comments in imperative code too. I've started commenting lots and then cutting it down later. If anything, it helps me reason about my code better.
On a totally separate tangent, I feel like the kernel of most of my FP code for data processing is essentially array programming like APL / J, just way way way more verbose.
Whenever we discuss this topic there are usually some defensive sounding remarks about the fact that inaccurate comments are much worse than no comment. This is certainly true but I don't share the conclusion that the only reasonable answer is to never leave a comment. I guess if that is already what you do, then its probably easier to conclude it is correct.
That sounds like a horrible system. Why'd they ever do that? The grading systems I have seen do the inverse, requiring certain amount of comments. That of course leads students writing gibberish just to fulfill the comment quota, but even that is better than penalizing for commenting.
The 'why' would be that there's an entire class of newbie developer who write opaque code, and use the comments as an excuse not to write code clearly.
I see too many developer with the general attitude 'This code is opaque - it needs a comment', where I vastly prefer 'This code needs a comment, how can I make it less opaque.'.
The first and second posts are here
http://www.pitheringabout.com/?p=693
http://www.pitheringabout.com/?p=749
Tangentially, the 'swedish colleague' the author brought in to help with the transition to Clojure is the immensely talented Hakan Raberg (sorry don't know how to type umlauts etc on my keyboard) , who is a good friend and worked with me at ThoughtWorks before he went indie.
It was a nice read. I liked the frankness - no claims to overnight Ninjitsu mastery - and yet I get the feeling it's all pretty solid.