Beautiful code — The Manifesto
In times past much has been said about beautiful code. Covering the topics of quality, correctness and expressiveness — more needs to be said. Opinions differ, so how can we judge what quality is and is not? I’ll try to outline some indicators which help pinpoint where on the spectrum your code lies and how to write beautiful code.
Preface
I’ve both written and commented on much code while running the blog. I’ve received both critique and praise. Through many debates it has become clear that we all view code and the quality thereof, differently. Having no common jargon, no definitive ‘language’ for describing code, conclusions are hardly ever reached. This makes for many unproductive discussions.
In as short a space as possible, I’ll try to describe the language which describes programming languages, setting forth a standard. I’ll try to keep it language agnostic, because beautiful code is not restricted to any one language.
” I once lived beside a forest, so I created a language to describe the trees. I recorded the paths of their branches, the patterns made on the ground by their fallen leaves. I mapped their growth, their life, their decline. ”
- The Alchemist, Zach Tellman
Like the Alchemist, we need a language wherein words and expressions take on meaning relevant for code. If you’ve read some of my previous posts you’ll know that I prefer concise code to bloated code, but is conciseness the ultimate goal? If not, we need to connect it with other qualities to get the full view. So lets starts with something tangible and work our way towards the engine room.
Short and to the point
Conciseness must first and foremost be weighed against clarity. By clarity I mean the ease of reading the code by a developer proficient in a given language. Clarity is essential for our micro-understanding of the project and affects the speed with which a team of developers work.
With every line of code comes a…
- maintenance cost
- development cost
- degree of complexity
- potential for failure/bugs
So every line of code is of course important as well as the number of lines. Counting lines can be fun while golfing, but it’s also a big factor in selecting which tool to use for the job. Using Clojure for outputting the contents of a directory would be overkill compared to shell-script for instance.
But we can’t go to the extreme of saying that we must then always use whatever tool which gives us the most concise code, since projects are their own eco-systems within an eco-system of developers, managers, designers, customers and more. So let’s try to balance the scales.
Example
Imagine a 20x20 grid where we need to compute the number of possible routes (without backtracking) to walk this grid from top left to bottom right. Mathematically, such a problem conforms to this equation
(2n)!
n!*n!But it is also solvable by a manual gridwalk.
That’s a straight forward problem, which can be solved in a number of ways. If we set conciseness as the primary objective, forgetting the properties that it’s connected with, we come across the language k.
Solution in K:
1. \p 0 f:*/1.+!: /factorial {f[x]%f[y]*f x-y}[40;20]
Both in counting lines and characters, it just does not get much shorter than that. But weighed against clarity we’ve made an expression which can scarcely be read by the k compiler itself. In a team of developers, code such as this will inevitably lead to misunderstandings, misreading but a single character dramatically changes your perception of the intent/algorithm.
Microsoft released C# (C-Sharp) several years ago to lend some competition to Java and to boost the speed of development. C# is the more concise and .Net integrated brother to C++. One C# developer didn’t believe that the above mentioned equation would compute as efficiently as a manual grid walk in C#, so he solved it in these 35 lines:
Solution in C#:
using System;
namespace Euler {
class GridPaths {
ulong[,] countCache = null;
private GridPaths(int x, int y) {
countCache = new ulong[x, y];
}
public static ulong Solve() {
return Solve(20, 20);
}
public static ulong Solve(int x, int y) {
GridPaths gp = new GridPaths(x, y);
return gp.solve();
}
private ulong solve() {
return countPaths(0, 0);
}
private ulong countPaths(int x, int y) {
if (x > countCache.GetUpperBound(0) || y > countCache.GetUpperBound(1)) {
return 1;
} else { //not on edge
if (countCache[x, y] == 0) { //count exists in cache?
countCache[x, y] = countPaths(x + 1, y) + countPaths(x, y + 1);
}
return countCache[x, y];
}
}
}
}
The actual logic of the code isn’t hard to follow, but using C# as the tool has added a huge degree of ceremony. For one thing, there’s no other reason to be implementing a class than the fact that this is the only place you can put methods and methods the only place where you can put code in C# — ditto C++ and Java. Besides the ‘class’ overhead, all methods add 2 – 3 lines of ceremony and all types must be explicitly declared.
So those were examples from both ends of the spectrum. It’s reasonable to conclude that the optimal tool and resulting code should be something which neither adds ceremony nor obfuscates our intention. In fact the code should as clearly as possible express just that: intent.
An example of that could be this Clojure snippet:
Solution in Clojure:
(reduce *
(map / (range 21 41) (range 1 21)))
We have 2 lines, both of which are expressing intent. We have a clear compartmental understanding of our problem/code, greatly minimizing the possibility of error and maximizing our ability to reason about the behavior. Had that taken us 5 – 6 lines to get, it would have been worth it.
We have here 3 very different ways to solve the same problem. k is a mathematical language, not something you would build a software development project with, but I picked it up to demonstrate a principle. Both Clojure and C# are used in industry for both large and small projects.
In the C# example we are looking at a simple mathematical problem. In a real-world scenario this would only be a very small subset of the project as a whole and already we’re approaching 40 lines. It is then no wonder, that C# projects often times fall in the 20.000+ lines of code category — and with every one of those lines comes cost, complexity, probability of failure and again, cost.
Line-count makes a big difference and projects developed using functional programming are smaller than their imperative counterparts by an order of magnitude. Sony Ericsson started using a functional programming language for some of their projects and they saw a productivity increase ranging from 10x — 25x compared to their standard C development. That means worst case a project which is a year in development will only take about 1½ months, and best case 2 weeks. Their code-size shrunk somewhere between 2x — 10x, making it more maintainable. Beautiful.
Compartmentalized
But more important than line count is the compartmental understanding of the problem we are trying to solve. Imagine a problem where the challenge was this:
We have a set of assets that all carry a certain value. We want to walk through our assets calculating their ‘liquid value’ which is given by the production cost and a factor indicating supply/demand, so we know which products to start shipping.
When this task comes up in the first design session your charismatic manager draws out his plan on the white board stating how he wants the module structured:

And that’s a certified flowchart, but we all know that although code might behave like that, it won’t look like it — riiight?. So how can we make this happen? An attempt in C# could look like this:
using System;
using System.Collections;
namespace liquidvalue
{
class MainClass
{
public static void Main(string[] args, ArrayList assets)
{
ArrayList lvalues = new ArrayList();
foreach(AssetClass asset in assets) {
double liquidValue = asset.SDFactor * asset.productionCost;
if (liquidValue >= (asset.productionCost * 2))
lvalues.Add(liquidValue);
}
double sumTotal = 0;
foreach(double lvalue in lvalues) {
sumTotal += lvalue;
}
Console.WriteLine(sumTotal.ToString());
}
}
}
This is a halfway house — almost compartmental. Where is the first item from the flowchart, the calculator? Well it’s in the first foreach loop. Where’s the filter? Also in the first foreach loop. And the accumulator is entirely in the last foreach. This is classical imperative programming. Make some lists, mutate them. Although a C# developer easily recognizes the code and can predict the resulting behavior the code is not naturally structured in a way which is analogous to the problem — hence the term ‘code’. The highest form of expression of the solution, would be to write a flow-chart compiler.
In Clojure we can express a solution like so:
(reduce #(+ %1 (:productioncost %2)) 0
(filter #(> (:liquidvalue %) (* 2 (:productioncost %)))
(map #(assoc % :liquidvalue (* (:sdfactor %) (:productioncost %))) assets)))
Or when properly refactored
(reduce (sum-field :productioncost) 0
(filter doubleMargin?
(map attach-liquid-value assets)))
But think about it. If most mainstream programming ends up looking totally different from flowcharts why do we keep drawing them? Because they provide the shortest route between our logical interpretation of the computing process and it’s written form, meaning flowcharts are intuitive to reason about. So it would be a huge benefit to code that way, because the code would be easier to reason about, therefore more sturdy and easier to maintain. Have a look at how C# (on top) reads compared to Clojure (on the bottom):
Even having stripped most of the ceremony, I think its obvious that the imperative approach does not come close to the same level of approachability as the functional variant. Functional code almost reads like a flow-chart. Because every line is an expression (not a command), I can skip the intermediate storage of data in variables. Because I don’t use any variables I have a natural ‘glue’ for my data-processing. Since there’s no mutation the code is robust and easy to reason about. There are no side-effects I need to contain or track, no state, just a description of the process. A CS professor once remarked
Imperative programming is telling the computer how to solve a given problem. Functional programming is telling it what the problem is.
That gives functional programming a lot of points in regards to readability and modularity. When we use functional expressions free of side-effects, we can also disregard the order of execution, giving us more ‘glue power’, where on the other hand imperative programs require the strictest attention to call-order, state, etc — adding many pitfalls which lead to bugs and higher maintenance cost and development time.
Avoiding frailty
A few months ago Tim Sweeney, CEO and Founder of Epic Games, gave a demonstration of why he thought the next mainstream language would be more functional than what we’re using now. Lets look at an example from game development:
Transforming vertices:
Vertex[] Transform (Vertex[] Vertices, int[] Indices, Matrix m)
{
Vertex[] Result = new Vertex[Indices.length];
for(int i=0; i < Indices.length; i++)
Result[i] = Transform(m, Vertices[Indices[i]]);
return Result;
};
Now this is easy to read and the intent is clear. You walk through one array of vertices, transforming them and assigning the result to a new container. But is it safe? If you commit this code to your local SCM then your routine build won’t fail, this compiles just fine. During runtime however the program could die with an exception
If…
- Vertices is null
- Indices is null
- Indices contains values outside ‘vertex’ range
- m is null
The “i <= Indices.length” could dereference a null pointer, the transformation can throw an ‘out of bounds’ exception.
Because of this imperative approach to programming, everything is volatile. In order to ensure that this method does not fail while the game is running, we need to scatter conditionals throughout, ensuring every operation gets the kind of arguments it expects. In the C# example we saw obvious ceremony but I won’t hesitate to label this hidden ceremony — covering over frail principles.
Here we gain a lot of momentum using sequence abstractions and non-explicit iteration. According to a PhD written at IBM many years ago, about 60%+ of our programs is data processing by traversal of sequences: enumerating, applying functions, filtering etc. So instead of extending the above example into a massive nest of conditionals, let us instead rewrite it to be more expressive:
Clojure:
(defn transform [vertices indices matrix]
(map #(transform matrix %)
(map #(nth vertices %)
(remove nil? indices)))
(pseudo code)
What happens if ‘indices’ is empty? Nothing, map has no work. What if it has empty items? Nothing, remove strips them. There’s still a potential for a runtime error, but so is there in Sweeneys code, so I’ll leave it for the sake of comparison. By adopting a language which is modeled around sequential abstraction many error paths are eliminated, the code is cut down in size and we can safely move on to the next item. By calling ‘remove nil’ we can be certain that all our direct vertex manipulation always has data to work with.
In a lisp there is simply no need to define ‘Result’ and inject transformed values into it, because everything is an expression — they always return the result of their operation. With mutability out of the way, we can start reclaiming the ‘time’ aspect of our programs. When things mutate, state changes and runtime errors follow.
A good question would be, how does the calling code respond if it passes a ‘v’ containing 120.000 vertices, but only gets 119.500 back? That’s a choice the developer will have to consider intelligently, but just that fact alone tells you that we’ve started unifying programming and behavioral logic, stripping out the role of being the compilers tutor. We can now spend those valuable mental resources on better things — ie things which our clients/customers/users actually benefit from. But more on language-motivated design patterns in a later post.
Exposing Mutability
Mr. Sweeneys example is very concise, yet clearly demonstrate the challenges inherent to mutability. Values change, assumptions become wrong, programs die. The vast majority of software bugs come from complexity inherent to mutability. There are 2 ways to contain this.
The first being correct use of (many) conditionals, building fences around the methods handling the volatile values. When we do this, it affects our code by making it
- bloated — grows enormously in size
- unreadable — due to the size you easily loose ‘the big picture’
- costly — ‘quick fixes’ are a thing of the past
- error prone — when you work to remove errors by adding more code, you add new possibilities of failure
When reading these considerations it’s worth noting that we haven’t even thought about concurrency yet — when we throw that into the mix, everything I just said quadruples. The complexity grows almost exponentially. Epic Games however aren’t rookies, so I think they covered all their bases, letting the final codebase weigh in at 250.000 lines of code! So seeing that option #1 is not really an option if we want beautiful code, lets move on to #2.
In a functional language the default way to handle mutability is: You don’t. Nothing ever mutates, data structures are immutable. Having immutable (& persistent) data structures is a big deal and dramatically changes the way you architect your code. This is also where the learning-curve rears it’s face.
When the default is immutability, we as developers have to manually, intelligently consider edge-cases, making good choices. This again touches on unifying behavioral logic and code, where our focus is now the interaction of various modules and not the interaction between 2 variables.
If we don’t agree on immutability as a necessary default for beautiful code, then there’s a problem. If I look at your variable knowing that even while I am watching it, it could be changing, how can I make a decision based on my perception? Somehow we need to stop the process while I get my perception in the middle, pouring concurrency down the drain. From where I’m sitting that will never be beautiful, it will be frail and unnerving.
Beautiful code is…
So to sum up this first installment of beautiful code. We see that code must be concise. Bloated code obfuscates intent. It must not however be so concise that intent becomes unclear and misunderstandings become inevitable.
It must be expressive, analogous to flowcharts. I used the word ‘compartmental’ because had I said ‘modular’ most people would think OO which would violate both point #1 and #3. Every line of code must speak volumes about the behavior it results in. I like what Christophe once told me
“Every line of code, whether it be PHP or Clojure has roughly the same potential for a bug. But 20 lines of Clojure may sometimes translate into 1000 lines of PHP.”
And finally, it must be safe. When our code habitually mutates objects, it becomes frail and dangerous. When you mix and mash sound principles by allowing developers freely to play both teams you’re letting your team loose in a landmine field (see Scala for an example). If your datastructures can be mutated, it is dangerous to assume they won’t be. If our code is to survive a swim in concurrent waters, we must also be explicit about time and state unless we want our f(x) to become f(x,time).
Beautiful code is
Concise — Free from both obvious and hidden ceremony
Expressive — Compartmental in its architecture, showing intent
Safe — Being explicit about state and time, defaulting to immutability
| | |
Comments are closed.


about 4 months ago
Hi all — I’m serving up the first comment!
I’ve received about 6 comments from C# developers all giving me their take on a better version of the liquid-value code above. A couple of them using Linq, which simplifies the code substantially. I’ve taken a drastic step in deleting all those comments — sorry guys!!
Not because they didn’t present valid points, but because they entirely missed the point — I tried to spell it out with the K example — We’re discussing principles here, not code examples. I have no doubt that my rusty C# can be written in a more idiomatic sense, using better libraries etc. But thats much beside the point. The point is, that while the language so freely permits and even encourages me to code that way — I will end up seeing that type of code in my systems. If I had the privilege of hiring a team of superior geniuses and unspeakable coding skills, then sure, they might consistently produce rock solid functional code even in C# — but that’s not a real world scenario.
So lets stick with the principles. And if anybody does provide examples, please connect them with something discussed in the post. That makes for a better discussion than just swapping code.
Thanks to all for taking the time, sorry for being so harsh
/Lau
about 4 months ago
Sorry, I didn’t see your answer before posting that C# example. Are you basically saying that Clojure stops you from writing verbose, unclear or simply bad code? I find that hard to believe.
about 4 months ago
@Jonas: Np :)
I think that about 50% of the flame I get, is people extrapolating my statements — I must try to be more verbose perhaps.
I’m saying first of all, that the world isn’t totally black and white. Of course you can write ugly and unreadable code in Clojure, no doubt about it. So what we’re talking about is which principles to build on — that becomes relevant for language designers, Quality Assurance Managers and coders striving for exellence. If you ask 100 Clojurians to solve the first 10 Eulers, I’ll bet you that about 75% of all solutions are virtually identical because the language encourages you to architect your code in a correct functional manner. On the flipside, in C# you’ll most likely see many diverse solutions (different styles, approaches etc) but also most of these will be cracked and frail. (And this isn’t more of a C# problem than a C, C++, Java, PHP, Python, etc, problem — it follows imperative programming, and I think anybody who’s done some work in the industry will recognize this?)
I’ve manged enough developers and lead enough projects to know, that coders who sit down with a problem and before even touching the keyboard say “I’ll architect this code compartmentally, I’ll preplan all evaluation paths, I’ll stay clear of mutability as far as humanly possible” etc etc is a very VERY rare breed ( I think I met 1, once — and he could make PHP look robust). We all fall back on our experience in some regard, and those tend to be criminally flawed with so many many developers who have found pleasure in code and ‘functionality’, but not beauty, elegance and robustness.
Those are my 2 cents, please dont turn them into 10 bucks :)
/Lau
about 4 months ago
@Jonas : Without any doubt, Clojure as many functional languages provides you with better abstraction than imperative one. Especially, for looping& co. These are a bit harder to learn, tho (they feel rigid at first because your mind had to learn a new way to look at the problem).
about 4 months ago
It came out as if I was bashing Clojure. I am certainly not. It seems to be a very well thought out language.
You may as well be right in that Clojure and other functional languages more often than not pushes the developer in a good direction. Now you just have to invent a way to explain how to program in them to the x% of programmers that find them extremely hard to understand. :)
I believe that LINQ will become a part of C#-programmer-culture over the coming years and that’s a good thing. Just look at Ruby. It would be fully possible to mimic your C# example in Ruby but almost no Ruby programmer ever would, it’s not in Ruby culture. A culture is not the same thing as if it’s “restricted” by the language itself but I kind of like the freedom.
about 4 months ago
C++ allows functions outside of classes and even code outside of function (in static initializers). Of course the order of execution of static initializers isn’t guaranteed, at least across dynamic libraries, so … um… Clojure’s still better. :-)
about 4 months ago
I don’t mean to nitpick, but your C# examples are quite dated. We now have stuff like generics, lambda expressions, and LINQ in C#, so the code would be a lot closer to the flowchart representation. However, the C# solution still isn’t as nice as what you’ve written in Closure ;)
Where in the old way you would have to create a new list to hold the matches and then iterate the old list, the new way, using LINQ, is much more functional:
var sum =
( from a in assets
let liquidValue = a.SDFactor * a.ProductionCost
let limit = 2 * a.ProductionCost
where liquidValue >= limit
select liquidvalue
).Sum()
about 4 months ago
You wrote: “By clarity I mean the ease of reading the code by a developer proficient in a given language.”
My impression is that you did not hold to this while considering the K code. Moreover, it seems likely to me that the K solution is not so clear as proficient K programmers would typically write.
I don’t read or write K, but I know its cousin, J, well enough to offer a solution:
(!@+: % *:@!) 20The parenthesized code is equivalent to the formula you showed, and that function is applied to twenty to get the specific value discussed above.
Since the code is nothing but a formulation of that function, I don’t see how it can be more obscure than the formulation you posted to assist your readers. (Its notation can, of course, be less familiar.)
There are strains that come with the absence of redundancy in languages such as J and K. I’ve no problem conceding that it is harder to learn such languages, and that reading them involves a manner of thought that is less like reading English than most programming languages.
What remains to be done, having recognized the difficulties, is weigh the benefits. I don’t follow the particulars as to why you think the costs are much greater than the benefits.
about 4 months ago
@Tracy: Thanks for stopping by :)
I agree with you, that I don’t have enough K experience to make that statement with any authority, but looking at your J code I would say it exemplifies my statement. Any 1 character misread will immediately give you a very bad interpretation of the intent, which isn’t good. I think the most concise language I’ve ever seen, which is still perfectly readable is Haskell — although you can be sure I’ll be digging in to J to make sure I’m not assuming.
I believe that the costs connected with unclear and obfuscated code are clear, both in terms of monetary value and time/resources. When larger projects are knitted together, all pieces must work perfectly together seeing that any unintended behavior can cause lengthy debugging sessions. Sometimes a teammember is sick or called away and somebody needs to step in a extend/fix his code, if he needs to spend 4 hours reading up on Java code or 3 hours decrypting J, then thats (by contrast) money straight down the drain. And while you’re pouring, your reputation suffers.
Thanks,
/Lau
about 4 months ago
I disagree with your example under the “Short and to the point” section. While the C# code indeed implements the manual grid walk, it seems that both your K code sample and the Clojure one are actually computing the binomial coefficient. Doing that in C# would also turn to be short and simple.
While I do agree to an extent with the point, it is probably worthwhile to present a more valid example.
about 4 months ago
@shmichael: Thanks for your comment.
Regarding the different approach, I also comment on that in the post just below the C# example. It could definitely have been solved in a shorter sweeter manner, but what I want people to realize is that when a language is structured like C#, you’ll often time see people defaulting to solutions like that. I’ve been around long enough to know, that when its so easy to code like that, there will always be people who do it — And although closer supervision, better planning and management, code reviews etc help remedy that over time — it still makes for worse software. Generally speaking.
about 4 months ago
The sensitivity of J to misreading is real. Names reduce that (at some cost.) It’s perfectly good J to write that function this way
walk_routes =: factorial after double over square after factorial
where the following were previously been defined:
factorial =: !
double =: +:
over =: % NB. a.k.a. division
square =: *:
after =: @
Experienced J coders are likely to be more comfortable with the primaries than with such stand-in names, however.
about 4 months ago
If we’re to approach a “language which describes programming languages” akin to what the Alchemist used to describe trees, I anticipate that we’ll be looking to identify the zones within which different species thrive, and the ecotones wherein we find overlap between them.
My impression is that you’re trying to describe the “sweet spot” where a language will best satisfy what you most value. Clojure seems to fit that role, and when I look at your arguments I get a rich sense of why that is so.
To look at beauty in code more generally, perhaps a variety of languages can be considered with an eye for the circumstances in which they can be used to particularly good effect. This may defuse the tendency to defend or advocate one language against others. With the assumption that each language has at least one environment in which it will flourish, you can survey the terrain, describing which languages have advantages in the diverse situations in which code will be written.
about 4 months ago
@Tracy: Very well put — Your point came across quite clearly!
I’m undecided as to whether or not your method of description is effective. In my mind there is a threshold of quality, where if a language comes in beneath this threshold I discard it entirely for the purpose of writing beautiful code. There are certainly cases where one would have to go with a C solution compared to say Haskell or Clojure, but it’ll never be beautiful and the majority of such decisions are either based on habit or necessity — the necessity perhaps coming from personal (lack of) skill/experience. It’ll work, it might have been the right thing to do at the time, but it’s not beautiful.
about 4 months ago
Pardon me, but I don’t fully understand the purpose of the lengthy post.
You
1) Admit that your C# and/or K examples could be solved much more nicely,
2) Admit that the C# example does not even do the same thing (let alone in the same way) as your Clojure example,
3) Admit that the Clojure code could also be much more verbose,
4) Admit that it would be common-place to write the C# snippet in a much more elegant, functional fashion,
and then you… Delete suggestions by experienced C# programmers showing how they would solve the problem?
Don’t get me wrong, I see the point that Clojure encourages more functional and often more concise, even more elegant solutions; what I do not see is how this post with all the snippets written in other languages does anything to advance this point.
If you argue that a *typical* C# programmer would write that C# code, then why do you intentionally provide *nice* Clojure code — I doubt anyone here expects a typical Clojure programmer to be as proficient as you are.
Clearly you do not argue that a *good* C# programmer would solve the problem the way you described — you explicitly removed many comments suggesting more appropriate ways of solving the problem, and even more, it has been pointed out that what your C# snippet does is different from what the K snippet does.
So, I really don’t see the point of bringing up C# at all — you could just as easily show a badly written Clojure implementation and show how avoiding “bad design” is a good thing — yes, Clojure makes it less natural to use mutable solutions, but it has been commented several times already that modern C# would also be written in a more functional approach.
If your goal is to show that Clojure makes it more awkward to write code encouraging bad habits, then do a post showing just that — how it would be unnaturally cumbersome to do many “bad” things (excessive use of side-effects, boilerplate code, etc.) — by comparing naively written Clojure code to badly written C# code, and showing how the former is still easier to refactor, easier to reason about, etc.; I have no doubt that this would be a clear indicator of Clojure’s favorable properties.
I don’t mean to be negative, but I have a feeling that many more experienced people find comparisons between intentionally badly written code in X and elegant code in Y something that does in no way promote Y over X among people who know both languages well enough to see the differences in code quality (I find it a slightly dishonest form of advertisement, to be honest).
If you want to compare *typical* code in both languages, you really can’t expect people to believe that your code, which happens to be very elegant and concise, is also the way a typical coder would write it, especially if your C# code is clearly not up to the same standards. The comparison is much more credible if you take existing large products written in both languages by relatively competent developers (just because that is easier to agree on than “average” competence) and look at the source code there; or if you try to do some other kind of case study — not something that lends itself to the format of a blog post, I admit.
Or at least also show what good C# code would look like when doing the comparison — this way, people can honestly decide whether they’d have written the first or the second version; and Clojure still compares favourably to even elegant C# code, so there’s no need to avoid a head-to-head comparison.
If you did not mean to compare the languages but the development styles (imperative vs functional), then refrain from claims regarding the languages themselves. If the C# implementation is “halfway house”, explain why this is a consequence of using imperative control structures; do not get side-tracked in an attempt to make C# look worse than it is. Similarly, show that the functional approach can be done perfectly well in other languages as well (even in Python, perhaps even in C#!), and that this is not something Clojure invented or perfected. If you take this approach, I would strongly suggest keeping discussions of coding styles in separate blog posts from actual language comparisons. Once you’ve shown how avoiding side-effects or typical imperative constructs leads to better code, write another blog post describing why you believe this is a point that makes Clojure a better choice than, say, Python or C#. If you take this approach, people can disagree with either premise, but you won’t meddle the waters by having both arguments (which are related but definitely distinct) conflated in a single post.
I’d say that it is also more convincing to a Python programmer to show that one style of development is nicer and *then* showing how Clojure encourages that style more than Python does; this avoids the arguments about whether Python can be written in a functional way, since you made it clear that you never stated the opposite, only that Clojure is better suited to that style.
Anyway, sorry for the long rant; I hope it clarifies what I (and I suspect several other readers) find bothersome about this type of post.
Keep up the writing,
M
about 4 months ago
@Mork: First off, thanks a lot for taking the time to write down your thoughts so carefully.
It’s not an easy thing to provide you with an equally good answer, as your comment mixes questions, suggestions and statements throughout.
1) Suggestions
Thanks — I’ll consider taking you up on writing future posts which more carefully distinguish what I’m trying to do, ie. compare languages, styles, etc. In part I feel this is important, because it makes posts deeper, not broader. I also hope that we as a community of developers eventually come to the point where we can compare snippets without turning it into a war — I think just examining the mentality of developers alone puts Darwins theory on thin ice, it’s been this way for as long as I can remember. I’d encourage all development communities to embrace discussions and criticism, joining the debate with arguments instead of assaults on either the author or the methodology — wishful thinking perhaps.
2) Ugly X vs Beautiful Y is bad.
Like i’ve commented on else. If you had 10 decent Clojure developers produce solutions for the first 10 Eulers, you’d probably see 10 almost identical solutions. I don’t consider the code I’ve posted here to be of any extraordinary quality, some of it was actually ‘pseudo code’ which happened to compile. That’s a credit to Clojure.
On the same note, I didn’t produce ugly looking C# code for the sake of being unfair. The code was to represent something which you get away with in C# quite easily, meaning as a businessman, manager, quality assurance guy etc, you can expect to have your projects littered with this type of stuff. That better code can be produced is a void argument, because in larger projects you have to plan for the Lowest Common Denominator, you’ll meet him all the time — he’s the guy who’s making you bug-fix for 6 months after the release. ( I say 6 months very gracefully as something as simple, ancient and antique as the NTPD service still gets bugfixes/security-patches once in a while and that’s like 10+ years old now?).
You can also check out the rest of my blog — This pattern seems to emerge throughout:
1. Compare 2 languages
2. Get official code from language X (Rosetta, PyEuler, Programming Scala etc)
3. Write my own Clojure code
4. Get comments stating that X-code is ugly, malformed and could be done many times better
5. Repeat for next language
Wouldn’t it be more constructive to hold off on the comments and ask yourself this question “If it is so easy and comes so natural, to so many people, to write buggy verbose ugly code in language X, I wonder if I shouldn’t try to examine language Y instead — I mean, there are days where I’m not 100% focused so how shaky is my own code on those days?”. I couldn’t have said that a month ago, but like I said, a pattern emerges.
3) You should use ‘typical code’
This IS typical code :)
Thanks,
Lau
about 4 months ago
Hi Lau,
Was the Tim Sweeney from a presentation he gave in 2005? To clarify, the example is how the imperative code is, with the list of issues he pointed out. If we’re talking about the same thing, he expressed how a game engine is mostly functional in nature, and the use of STM for managing world state.
He was looking into Haskell, but didn’t particularly like the syntax and preferred “lenient evaluation” (which I don’t understand) to lazy evaluation. It sounds as though people often have trouble knowing the performance characteristics of Haskell code. That makes Clojure quite interesting for game development, which has lazy sequences but not full lazy evaluation. Though I don’t suspect the next Unreal engine to be written with Clojure, since afaik Java isn’t common on consoles. Maybe Clojure CLR will have better luck?
about 4 months ago
Hey Nathan,
I think the presentation was given in early 2006, but I’m unsure — From your recap it sounds like we’re talking about the same thing. I think I’d be happier trying to get some game performance out of Haskell than Clojure atm — Did you see the Brians Brain errata? Still puzzles me…
/Lau
about 4 months ago
Lau,
I hadn’t yet read Brian’s Brain… that is a bit disappointing. Had you tried adding type hints or using defhinted? Haskell’s type inference is a nice advantage, though Tim Sweeney’s presentation also claimed that “type inference doesn’t scale”.
Some time ago there was a series of graphs of performance vs. lines of code — based on the Computer Language Benchmarks Game.
http://gmarceau.qc.ca/blog/2009/05/speed-size-and-dependability-of.html
There was a comment that optimizing in Haskell was somewhat of a black art, though that is probably getting better now with Bryan O’Sullivan’s Criterion benchmarking library
http://www.serpentine.com/blog/
Mauricio Fernández is always raving about the performance of OCaml: http://eigenclass.org/R2/
It would certainly be nice to have a better understanding of the various functional languages and their differences… Haskell, OCaml, F#, SML, and of course Clojure. I only have a surface level understanding so far.
about 4 months ago
Nathan,
If you want to chip in, read first the functional version and then the transient where I optimize everything I can think of, then the errata where I point of 3 new ways of optimizing. I’ve consulted with other Clojurians on this as well, as it’s quite disappointing.
Secondly, note that a similar simulation done in SBCL can compute in 40 seconds what Clojure takes about 5 minutes to do — I expect similar results from Haskell. Lastly if you recall the Widefinder project, OCalm parsed 25 Gb (I think) of logs in about 5 minutes, the fastest Clojure version ran at about 14 minutes. There are cases, where the compiler seems to go off and do weird things and if I could find the time to narrow down the examples I would send them off to Rich — Someone should :)
/Lau
about 4 months ago
“Ultimately the first factor of performance is the maturity of the implementation.”
I looked briefly at the SBCL/Clozure version, but I couldn’t tell if it was pure functional or making use of mutable state. I’m still not proficient in reading Lisp (or even Clojure).
More profiling necessary?
about 4 months ago
The SBCL version mutates which in itself should explain a lot of the difference, but then problem is that the transient/java-array versions don’t improve the performance, meaning that even when mutating performance is down.
I suspect that the profiler will only tell you that the filtering in on-neighbors is taking some time, but trying a manual loop again didn’t improve performance.
/Lau
about 4 months ago
A recent Scala discussion led me to learn that there is nothing slower than a Java Generic Array storing primitives.
You might want to try replacing the primitives with something else.
about 3 months ago
@Lau,
Thanks for the response. After sleeping over it for a few nights, I’m decidedly less argumentative than I was when I wrote the post :-)
It seems I pretty much agree with you, I just think it is hard to convince readers of a blog that your nice code /is/ typical code — I’m not saying it’s not true, but given language X, which people are familiar with, and language Y, which people aren’t, it’s hard for people to judge whether a snippet in Y really is “typical”, whereas the flaws of snippet X stand out due to familiarity. I suggested showing “typical” code in both languages, but I realize that my suggestion wouldn’t really solve the problem, as you showed :-)
I absolutely agree that the pattern you described emerges, and I’m afraid that it’s common to various forms of programming language comparisons. My comments included suggestions by which I hoped the problem would be reduced (by making different kinds of comparisons), but I realize now that it’ll never solve it completely.
Thanks,
Mork
about 3 months ago
Hi again Lau,
In your sentence “Conciseness must first and foremost be weighed against clarity” You need to remember that a definition of concision includes clarity. A concise statement is not verbose, but neither is it cluttered. Some dictionaries are called concise because they have a lot of information to impart, but are trying to do it clearly as well as using less words.
A concise language would be an ideal. All the necessary information, clearly presented.